diff --git a/CMakeLists.txt b/CMakeLists.txt index 28aaeb519..eec0265c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,13 @@ option(DP_STAT "DP_STAT" OFF) option(SINGLE_THREADED "SINGLE_THREADED" OFF) option(EIGEN_BLAS "EIGEN_BLAS" OFF) option(WITH_ZSTD "WITH_ZSTD" OFF) +option(KEEP_TARGET_ID "KEEP_TARGET_ID" OFF) +option(HIT_KEEP_TARGET_ID "HIT_KEEP_TARGET_ID" OFF) +option(LONG_SEEDS "LONG_SEEDS" OFF) +option(WITH_AVX512 "WITH_AVX512" OFF) +option(WITH_DNA "WITH_DNA" OFF) +option(WITH_MCL "WITH_MCL" OFF) +option(WITH_MIMALLOC "WITH_MIMALLOC" OFF) set(MAX_SHAPE_LEN 19) set(BLAST_INCLUDE_DIR "" CACHE STRING "BLAST_INCLUDE_DIR") set(BLAST_LIBRARY_DIR "" CACHE STRING "BLAST_LIBRARY_DIR") @@ -58,6 +65,30 @@ if(EXTRA) add_definitions(-DEXTRA) endif() +if(KEEP_TARGET_ID) + add_definitions(-DKEEP_TARGET_ID) +endif() + +if(HIT_KEEP_TARGET_ID) + add_definitions(-DHIT_KEEP_TARGET_ID) +endif() + +if(LONG_SEEDS) + add_definitions(-DLONG_SEEDS) +endif() + +if(WITH_AVX512) + add_definitions(-DWITH_AVX512) +endif() + +if(WITH_DNA) + add_definitions(-DWITH_DNA) +endif() + +if(WITH_MCL) + add_definitions(-DWITH_MCL) +endif() + add_definitions(-DMAX_SHAPE_LEN=${MAX_SHAPE_LEN}) IF(STATIC_LIBGCC) @@ -65,9 +96,9 @@ IF(STATIC_LIBGCC) endif() if(BUILD_STATIC) - set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") - set(BUILD_SHARED_LIBRARIES OFF) - set(CMAKE_EXE_LINKER_FLAGS "-static") + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + set(BUILD_SHARED_LIBRARIES OFF) + set(CMAKE_EXE_LINKER_FLAGS "-static") endif() check_cxx_compiler_flag("-std=gnu++14" HAS_GNUPP14) @@ -113,14 +144,17 @@ if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5) endif() set(DISPATCH_OBJECTS -"src/dp/swipe/banded_3frame_swipe.cpp" -"src/search/stage2.cpp" -"src/tools/benchmark.cpp" -"src/dp/swipe/swipe_wrapper.cpp" -"src/util/tantan.cpp" -"src/dp/scan_diags.cpp" -"src/dp/ungapped_simd.cpp" -) + "src/dp/swipe/banded_3frame_swipe.cpp" + "src/search/stage2.cpp" + "src/tools/benchmark.cpp" + "src/dp/swipe/swipe_wrapper.cpp" + "src/masking/tantan.cpp" + "src/dp/scan_diags.cpp" + "src/dp/ungapped_simd.cpp" + "src/dp/pfscan/pfscan.cpp" + "src/dp/swipe/anchored_wrapper.cpp" + "src/dp/score_profile.cpp" + ) if(EXTRA) LIST(APPEND DISPATCH_OBJECTS "src/tools/benchmark_swipe.cpp") @@ -130,17 +164,27 @@ add_library(arch_generic OBJECT ${DISPATCH_OBJECTS}) target_compile_options(arch_generic PUBLIC -DDISPATCH_ARCH=ARCH_GENERIC -DARCH_ID=0 -DEigen=Eigen_GENERIC) target_include_directories(arch_generic PRIVATE "${CMAKE_SOURCE_DIR}/src/lib") if(X86) -add_library(arch_sse4_1 OBJECT ${DISPATCH_OBJECTS}) -target_include_directories(arch_sse4_1 PRIVATE "${CMAKE_SOURCE_DIR}/src/lib") -add_library(arch_avx2 OBJECT ${DISPATCH_OBJECTS}) -target_include_directories(arch_avx2 PRIVATE "${CMAKE_SOURCE_DIR}/src/lib") -if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) - target_compile_options(arch_sse4_1 PUBLIC -DDISPATCH_ARCH=ARCH_SSE4_1 -DARCH_ID=1 -D__SSSE3__ -D__SSE4_1__ -D__POPCNT__ -DEigen=Eigen_SSE4_1) + add_library(arch_sse4_1 OBJECT ${DISPATCH_OBJECTS}) + target_include_directories(arch_sse4_1 PRIVATE "${CMAKE_SOURCE_DIR}/src/lib") + add_library(arch_avx2 OBJECT ${DISPATCH_OBJECTS}) + target_include_directories(arch_avx2 PRIVATE "${CMAKE_SOURCE_DIR}/src/lib") + if(WITH_AVX512) + add_library(arch_avx512 OBJECT ${DISPATCH_OBJECTS}) + target_include_directories(arch_avx512 PRIVATE "${CMAKE_SOURCE_DIR}/src/lib") + endif() + if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) + target_compile_options(arch_sse4_1 PUBLIC -DDISPATCH_ARCH=ARCH_SSE4_1 -DARCH_ID=1 -D__SSSE3__ -D__SSE4_1__ -D__POPCNT__ -DEigen=Eigen_SSE4_1) target_compile_options(arch_avx2 PUBLIC -DDISPATCH_ARCH=ARCH_AVX2 -DARCH_ID=2 /arch:AVX2 -D__SSSE3__ -D__SSE4_1__ -D__POPCNT__ -DEigen=Eigen_AVX2) -else() - target_compile_options(arch_sse4_1 PUBLIC -DDISPATCH_ARCH=ARCH_SSE4_1 -DARCH_ID=1 -mssse3 -mpopcnt -msse4.1 -DEigen=Eigen_SSE4_1) + if(WITH_AVX512) + target_compile_options(arch_avx512 PUBLIC -DDISPATCH_ARCH=ARCH_AVX512 -DARCH_ID=3 /arch:AVX512 -D__SSSE3__ -D__SSE4_1__ -D__POPCNT__ -DEigen=Eigen_AVX512) + endif() + else() + target_compile_options(arch_sse4_1 PUBLIC -DDISPATCH_ARCH=ARCH_SSE4_1 -DARCH_ID=1 -mssse3 -mpopcnt -msse4.1 -DEigen=Eigen_SSE4_1) target_compile_options(arch_avx2 PUBLIC -DDISPATCH_ARCH=ARCH_AVX2 -DARCH_ID=2 -mssse3 -mpopcnt -msse4.1 -msse4.2 -mavx -mavx2 -DEigen=Eigen_AVX2) -endif() + if(WITH_AVX512) + target_compile_options(arch_avx512 PUBLIC -DDISPATCH_ARCH=ARCH_AVX512 -DARCH_ID=3 -mssse3 -mpopcnt -msse4.1 -msse4.2 -mavx -mavx2 -mavx512f -mavx512bw -DEigen=Eigen_AVX512) + endif() + endif() endif(X86) set(OBJECTS @@ -148,7 +192,6 @@ set(OBJECTS src/basic/config.cpp src/stats/score_matrix.cpp src/data/queries.cpp - src/data/reference.cpp src/data/seed_histogram.cpp src/output/daa/daa_record.cpp src/util/command_line_parser.cpp @@ -204,35 +247,28 @@ set(OBJECTS src/tools/tools.cpp src/util/system/getRSS.cpp src/lib/tantan/LambdaCalculator.cc - src/util/algo/upgma.cpp - src/util/algo/upgma_mc.cpp src/util/algo/edge_vec.cpp src/util/string/string.cpp src/align/extend.cpp - src/test/simulate.cpp src/test/test.cpp src/align/ungapped.cpp src/align/gapped_score.cpp src/align/gapped_final.cpp src/align/full_db.cpp src/align/culling.cpp - src/cluster/medoid.cpp src/cluster/cluster_registry.cpp - src/cluster/multi_step_cluster.cpp - src/cluster/mcl.cpp + src/cluster/cascaded/cascaded.cpp src/align/output.cpp src/tools/roc.cpp src/test/data.cpp src/test/test_cases.cpp src/chaining/smith_waterman.cpp - src/tools/merge_tsv.cpp src/output/xml_format.cpp src/align/gapped_filter.cpp src/util/parallel/filestack.cpp src/util/parallel/parallelizer.cpp src/util/parallel/multiprocessing.cpp src/tools/benchmark_io.cpp - src/align/memory.cpp src/lib/alp/njn_dynprogprob.cpp src/lib/alp/njn_dynprogproblim.cpp src/lib/alp/njn_dynprogprobproto.cpp @@ -272,21 +308,104 @@ set(OBJECTS src/data/dmnd/dmnd.cpp src/data/sequence_file.cpp src/tools/find_shapes.cpp - src/data/block.cpp + src/data/block/block.cpp + src/data/block/block_wrapper.cpp src/run/config.cpp src/data/sequence_set.cpp src/align/global_ranking/table.cpp src/output/daa/daa_write.cpp src/search/seed_complexity.cpp src/tools/view.cpp - src/util/string/tsv.cpp + src/util/tsv/tsv.cpp src/tools/join.cpp src/basic/value.cpp src/masking/motifs.cpp src/align/alt_hsp.cpp + src/data/fasta/fasta_file.cpp + # src/cluster/incremental/run.cpp + src/cluster/output.cpp + # src/cluster/incremental/config.cpp + src/cluster/realign.cpp + src/cluster/reassign.cpp + src/util/tsv/read_tsv.cpp + src/tools/greedy_vertex_cover.cpp + src/cluster/cascaded/recluster.cpp + src/cluster/helpers.cpp + src/util/kmer/filter.cpp + src/align/kmer_filter.cpp + src/search/kmer_ranking.cpp + src/chaining/hamming_ext.cpp + src/dna/smith_watermann.cpp + src/stats/dna_scoring/build_score.cpp + src/lib/blast/blast_message.cpp + src/lib/blast/blast_stat.cpp + src/lib/blast/blastn_score.cpp + src/lib/blast/ncbi_std.cpp + src/util/tsv/table.cpp + src/util/tsv/file.cpp + src/util/tsv/record.cpp + src/cluster/cascaded/helpers.cpp + src/cluster/cascaded/wrapper.cpp src/output/daa/merge.cpp + src/chaining/backtrace.cpp + src/util/tsv/merge.cpp + src/util/tsv/join.cpp + src/dp/scalar/smith_waterman.cpp + src/cluster/incremental/config.cpp + src/cluster/incremental/run.cpp + src/align/short.cpp ) +if(WITH_DNA) + list(APPEND OBJECTS src/lib/wfa2/alignment/affine2p_penalties.c + src/lib/wfa2/alignment/affine_penalties.c + src/lib/wfa2/alignment/cigar.c + src/lib/wfa2/alignment/score_matrix.c + src/lib/wfa2/bindings/cpp/WFAligner.cpp + src/lib/wfa2/system/mm_allocator.c + src/lib/wfa2/system/mm_stack.c + src/lib/wfa2/system/profiler_counter.c + src/lib/wfa2/system/profiler_timer.c + src/lib/wfa2/utils/bitmap.c + src/lib/wfa2/utils/commons.c + src/lib/wfa2/utils/dna_text.c + src/lib/wfa2/utils/heatmap.c + src/lib/wfa2/utils/sequence_buffer.c + src/lib/wfa2/utils/string_padded.c + src/lib/wfa2/utils/vector.c + src/lib/wfa2/wavefront/wavefront.c + src/lib/wfa2/wavefront/wavefront_align.c + src/lib/wfa2/wavefront/wavefront_aligner.c + src/lib/wfa2/wavefront/wavefront_attributes.c + src/lib/wfa2/wavefront/wavefront_backtrace.c + src/lib/wfa2/wavefront/wavefront_backtrace_buffer.c + src/lib/wfa2/wavefront/wavefront_backtrace_offload.c + src/lib/wfa2/wavefront/wavefront_components.c + src/lib/wfa2/wavefront/wavefront_compute.c + src/lib/wfa2/wavefront/wavefront_compute_affine.c + src/lib/wfa2/wavefront/wavefront_compute_linear.c + src/lib/wfa2/wavefront/wavefront_compute_affine2p.c + src/lib/wfa2/wavefront/wavefront_compute_edit.c + src/lib/wfa2/wavefront/wavefront_debug.c + src/lib/wfa2/wavefront/wavefront_display.c + src/lib/wfa2/wavefront/wavefront_extend.c + src/lib/wfa2/wavefront/wavefront_heuristic.c + src/lib/wfa2/wavefront/wavefront_pcigar.c + src/lib/wfa2/wavefront/wavefront_penalties.c + src/lib/wfa2/wavefront/wavefront_plot.c + src/lib/wfa2/wavefront/wavefront_slab.c + src/dna/smith_watermann.cpp + src/dna/wfa2_test.cpp + src/dna/TEMP_minimap_seeding.cpp + src/lib/wfa2/wavefront/wavefront_unialign.c + src/lib/wfa2/wavefront/wavefront_bialigner.c + src/lib/wfa2/wavefront/wavefront_bialign.c) +endif() + +if(WITH_MCL) + list(APPEND OBJECTS src/contrib/mcl/mcl.cpp) +endif() + list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}") if(BLAST_INCLUDE_DIR) @@ -298,14 +417,18 @@ if(WITH_ZSTD OR BLAST_INCLUDE_DIR) endif() if(X86) - add_executable(diamond $ $ $ ${OBJECTS} ${BLAST_OBJ} ${ZSTD_OBJ}) + if(WITH_AVX512) + add_executable(diamond $ $ $ $ ${OBJECTS} ${BLAST_OBJ} ${ZSTD_OBJ}) + else() + add_executable(diamond $ $ $ ${OBJECTS} ${BLAST_OBJ} ${ZSTD_OBJ}) + endif() else() add_executable(diamond $ ${OBJECTS} ${BLAST_OBJ} ${ZSTD_OBJ}) endif() target_include_directories(diamond PRIVATE - "${ZLIB_INCLUDE_DIR}" - "${CMAKE_SOURCE_DIR}/src/lib") + "${ZLIB_INCLUDE_DIR}" + "${CMAKE_SOURCE_DIR}/src/lib") if(BLAST_INCLUDE_DIR) function(find_blast_lib var library) @@ -338,9 +461,9 @@ if(BLAST_INCLUDE_DIR) find_blast_lib(XUTIL_LIBRARY xutil) find_blast_lib(LMDB_LIBRARY lmdb) target_link_libraries(diamond ${SEQDB_LIBRARY} ${BLASTDB_LIBRARY} ${BLASTDB_FORMAT_LIBRARY} ${GENERAL_LIBRARY} - ${XOBJUTIL_LIBRARY} ${XOBJMGR_LIBRARY} ${GENOME_COLLECTION_LIBRARY} ${SEQ_LIBRARY} ${SEQCODE_LIBRARY} - ${SEQSET_LIBRARY} ${SEQUTIL_LIBRARY} ${PUB_LIBRARY} ${MEDLINE_LIBRARY} ${BIBLIO_LIBRARY} ${SUBMIT_LIBRARY} - ${XSER_LIBRARY} ${XNCBI_LIBRARY} ${XUTIL_LIBRARY} ${LMDB_LIBRARY}) + ${XOBJUTIL_LIBRARY} ${XOBJMGR_LIBRARY} ${GENOME_COLLECTION_LIBRARY} ${SEQ_LIBRARY} ${SEQCODE_LIBRARY} + ${SEQSET_LIBRARY} ${SEQUTIL_LIBRARY} ${PUB_LIBRARY} ${MEDLINE_LIBRARY} ${BIBLIO_LIBRARY} ${SUBMIT_LIBRARY} + ${XSER_LIBRARY} ${XNCBI_LIBRARY} ${XUTIL_LIBRARY} ${LMDB_LIBRARY}) add_definitions(-DWITH_BLASTDB) set_target_properties(diamond PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE) if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) @@ -366,6 +489,13 @@ if(WITH_ZSTD OR BLAST_INCLUDE_DIR) add_definitions(-DWITH_ZSTD) endif() +if(WITH_MIMALLOC) + find_package(mimalloc 2.0 REQUIRED) + add_definitions(-DWITH_MIMALLOC) + target_link_libraries(diamond mimalloc) + target_include_directories(diamond PRIVATE ${MIMALLOC_INCLUDE_DIR}) +endif() + target_link_libraries(diamond ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS diamond DESTINATION bin) diff --git a/Dockerfile b/Dockerfile index 0e7e934c2..b19beda1c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,20 +2,20 @@ FROM ubuntu:latest as build-diamond ARG DEBIAN_FRONTEND=noninteractive ENV TZ=Europe/Moscow -RUN apt-get update && apt-get install -y g++ automake cmake zlib1g-dev subversion libzstd-dev +RUN apt-get update && apt-get install -y g++ automake cmake zlib1g-dev git libzstd-dev WORKDIR /opt/diamond ADD . . -RUN svn co https://anonsvn.ncbi.nlm.nih.gov/repos/v1/trunk/c++ -WORKDIR c++ -RUN ./cmake-configure --without-debug --with-projects="objtools/blast/seqdb_reader;objtools/blast/blastdb_format" -WORKDIR CMake-GCC930-Release/build +RUN git clone https://github.com/ncbi/ncbi-cxx-toolkit-public.git +WORKDIR ncbi-cxx-toolkit-public +RUN ./cmake-configure --without-debug --with-projects="objtools/blast/seqdb_reader;objtools/blast/blastdb_format" --with-build-root=build +WORKDIR build/build RUN make -j4 -RUN cp /opt/diamond/c++/CMake-GCC930-Release/inc/ncbiconf_unix.h /opt/diamond/c++/include +RUN cp /opt/diamond/ncbi-cxx-toolkit-public/build/inc/ncbiconf_unix.h /opt/diamond/ncbi-cxx-toolkit-public/include WORKDIR /opt/diamond/build -RUN cmake -DCMAKE_BUILD_TYPE=Release -DBLAST_INCLUDE_DIR=/opt/diamond/c++/include -DBLAST_LIBRARY_DIR=/opt/diamond/c++/CMake-GCC930-Release/lib .. +RUN cmake -DCMAKE_BUILD_TYPE=Release -DBLAST_INCLUDE_DIR=/opt/diamond/ncbi-cxx-toolkit-public/include -DBLAST_LIBRARY_DIR=/opt/diamond/ncbi-cxx-toolkit-public/build/lib .. RUN make -j4 && make install FROM ubuntu:latest diff --git a/src/ChangeLog b/src/ChangeLog index d29fe8877..8a61c9099 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,63 @@ +[2.1.0] +- Added the `cluster` workflow to cluster protein sequences. +- Added the `realign` workflow to generate clustering output. +- Added the `recluster` workflow to correct errors in clusterings. +- Added the `reassign` workflow to reassign cluster members to their closest centroid. +- Added the option `-M/--memory-limit` to set a memory limit for clustering workflows. +- Added the `approx-id` option to filter alignments by approximate sequence identity + and to set an approximate sequence identity threshold for clustering. +- Added the `--member-cover` option to set the coverage threshold of the cluster + member sequence. +- Added the `cluster-steps` option to set steps for cascaded clustering. +- Added the `clusters` option to specify clustering input file. +- The `blastx` mode will now mask any open reading frame below the minimum required + length as specified by `--min-orf`. +- The `blastx` mode will only count unmasked letters towards the block size. +- Fixed a bug that caused an error when using the global ranking mode. +- Added the fast mode as the first round in iterative searches. +- Fixed a bug that caused the program not to function on systems without support + for SSE4.1. +- Improved multi-threaded load balancing of gapped extension computations. +- Improved performance of seed extension stage when HSP filter settings are used. +- Added the option `--soft-masking` with possible values `0` and `tantan` to permit + soft-masking using the tantan algorithm. +- Fixed a bug that could cause an `inflate error` in multiprocessing mode. +- Added the option `--swipe` to compute full Smith Waterman alignments of all + queries against all targets. +- Added the sensitivity mode `--faster`. +- Added the option `-M` to set memory limit for the `cluster`, `realign`, + `recluster` and `reassign` workflows. +- Added the output fields `approx_pident` and `corrected_bitscore` to the tabular + format. +- Added the `--lin-stage1` option to linearize comparisons in the seeding stage + by only considering hits against the longest query sequence for identical seeds. +- Added the `--kmer-ranking` option to rank sequences when `--lin-stage1` is used + (only supported when compiled with `-DKEEP_TARGET_ID=ON`). +- Added the option `--no-block-size-limit` to deactivate upper limits for the block + size when the `--memory-limit` option is used. +- Added the `greedy-vertex-cover` workflow to compute clustering based on + alignments. +- Added the `--edge-format` option to set edge format for greedy vertex cover. +- Added the `--edges` option to set input file for greedy vertex cover. +- Added the `--centroid-out` option to output centroid sequences for greedy + vertex cover. +- Added the `--unaligned-targets` option to generate an output file of unaligned + targets. +- Fixed an issue that failed compilation using the Intel Compiler. +- Fixed an issue that could cause a segmentation fault in rare cases. +- The `--header` option can now be used with the parameter `simple` to enable + simple headers for the tabular format, or without a parameter to enable + headers for the clustering format. +- Added the option `--mp-self` to optimize self-alignment in multiprocessing + mode. +- Added the option `--query-or-subject-cover` to report alignments if the query + or the subject cover (or both) are above the given threshold. +- Removed support for the `--comp-based-stats 2` option (now equivalent to + `--comp-based-stats 3`). +- Removed hit culling in case of overlapping target ranges in frameshift + alignment mode. +- Added the option `--anchored-swipe` to activate anchored SWIPE extension. + [2.0.15] - Fixed a bug (present since v2.0.12) that caused the `diamond view` workflow to report a zero bit score for all alignments. diff --git a/src/align/align.cpp b/src/align/align.cpp index 4aeb8e78d..52bc6519a 100644 --- a/src/align/align.cpp +++ b/src/align/align.cpp @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -23,14 +23,17 @@ along with this program. If not, see . #include "align.h" #include "../data/reference.h" #include "../output/output_format.h" -#include "../util/queue.h" #include "../output/output.h" #include "legacy/query_mapper.h" #include "../util/async_buffer.h" +#include "../util/parallel/thread_pool.h" #if _MSC_FULL_VER == 191627042 #include "../util/algo/merge_sort.h" #endif #include "extend.h" +#ifdef WITH_DNA +#include "../dna/wfa2_test.h" +#endif #include "../util/algo/radix_sort.h" #include "target.h" #define _REENTRANT @@ -39,73 +42,171 @@ along with this program. If not, see . using std::get; using std::tuple; using std::unique_ptr; +using std::thread; +using std::lock_guard; +using std::mutex; +using std::pair; +using std::vector; DpStat dp_stat; -struct Align_fetcher -{ - static void init(size_t qbegin, size_t qend, Search::Hit* begin, Search::Hit* end) - { - it_ = begin; - end_ = end; - queue_ = unique_ptr(new Queue(qbegin, qend)); +static vector partition; + +namespace Extension { + +TextBuffer* pipeline_short(BlockId query, Search::Hit* begin, Search::Hit* end, Search::Config& cfg, Statistics& stats); + +} + +#ifndef EXTRA +#define OLD +#endif + +static void make_partition(Search::Hit* begin, Search::Hit* end) { + partition.clear(); + Search::Hit* p = begin; + partition.push_back(0); + const BlockId c = align_mode.query_contexts; + while (p < end) { + Search::Hit* q = std::min(p + config.min_task_trace_pts, end - 1); + const BlockId query = q->query_ / c; + do { + ++q; + } while (q < end && q->query_ / c == query); + partition.push_back(q - begin); + p = q; } - bool operator()(size_t query) - { - const unsigned q = (unsigned)query, - c = align_mode.query_contexts; - begin = it_; - while (it_ < end_ && it_->query_ / c == q) - ++it_; - end = it_; - this->query = query; - target_parallel = (end - begin > config.query_parallel_limit) && (config.frame_shift == 0 || (config.toppercent < 100 && config.query_range_culling)); - return target_parallel; +} + +#ifndef OLD + +struct HitIterator { + static bool single_query() { + return config.swipe_all || align_mode.mode == AlignMode::blastn; + } + HitIterator(BlockId qbegin, BlockId qend, Search::Hit* begin, Search::Hit* end): + p(single_query() ? qbegin : 0), + data(begin), + query_begin(qbegin), + query_end(qend) + {} + struct Hits { + BlockId query; + Search::Hit* begin, * end; + }; + vector operator*() { + vector r; + int64_t i = p++; + if (single_query()) { + if (i < query_end) + r.push_back(Hits{ (BlockId)i,nullptr,nullptr }); + return r; + } + if (i >= partition.size() - 1) + return r; + const BlockId c = align_mode.query_contexts; + Search::Hit* begin = data + partition[i], * end = data + partition[i + 1]; + BlockId last_query = begin > data ? (begin - 1)->query_/c + 1 : query_begin; + const int64_t query_count = (end - 1)->query_/c + 1 - last_query; + r.reserve(query_count); + for (; last_query < begin->query_/c; ++last_query) + r.push_back(Hits{ last_query, nullptr,nullptr }); + auto it = merge_keys(begin, end, [c](const Search::Hit& h) { return h.query_/c; }); + while (it.good()) { + for (; last_query < it.key(); ++last_query) + r.push_back(Hits{ last_query, nullptr,nullptr }); + r.push_back(Hits{ (BlockId)it.key(), it.begin(), it.end() }); + ++it; + ++last_query; + } + if (i == partition.size() - 2) { + r.reserve(r.size() + query_end - (r.back().query + 1)); + for (BlockId j = r.back().query + 1; j < query_end; ++j) + r.push_back(Hits{ j, nullptr,nullptr }); + } + return r; + } + bool good(const vector& hits) const { + return !hits.empty(); + } + std::atomic p; + Search::Hit* data; + const BlockId query_begin, query_end; +}; + +#else + +struct HitIterator +{ + static bool single_query() { + return config.swipe_all || align_mode.mode == AlignMode::blastn; } - bool get() + struct Hits { + BlockId query; + Search::Hit* begin, *end; + }; + HitIterator(BlockId qbegin, BlockId qend, Search::Hit* begin, Search::Hit* end): + query_begin_(qbegin), + query_end_(qend), + query_(qbegin), + it_(begin), + end_(end) + {} + vector operator*() { - return queue_->get(*this) != Queue::end; + static const int64_t MAX_QUERIES = 256; + vector r; + int64_t n = 0, query_count = 0; + do { + Hits hits; + const unsigned q = (unsigned)query_++, + c = align_mode.query_contexts; + hits.begin = it_; + while (it_ < end_ && it_->query_ / c == q) + ++it_; + hits.end = it_; + hits.query = q; + n += hits.end - hits.begin; + ++query_count; + r.push_back(hits); + } while (it_ < end_ && n < config.min_task_trace_pts && query_count < MAX_QUERIES); + return r; } - void release() { - if (target_parallel) - queue_->release(); + bool good(const vector& hits) const { + return query_ < query_end_; } - size_t query; - Search::Hit* begin, *end; - bool target_parallel; -private: - static Search::Hit* it_, *end_; - static unique_ptr queue_; +private: + const BlockId query_begin_, query_end_; + BlockId query_; + Search::Hit* it_, * const end_; }; -unique_ptr Align_fetcher::queue_; -Search::Hit* Align_fetcher::it_; -Search::Hit* Align_fetcher::end_; +#endif -TextBuffer* legacy_pipeline(Align_fetcher &hits, Search::Config& cfg, Statistics &stat) { +TextBuffer* legacy_pipeline(const HitIterator::Hits& hits, Search::Config& cfg, Statistics &stat) { if (hits.end == hits.begin) { TextBuffer *buf = nullptr; - if (!blocked_processing && *output_format != Output_format::daa && config.report_unaligned != 0) { + if (!cfg.blocked_processing && *cfg.output_format != OutputFormat::daa && config.report_unaligned != 0) { buf = new TextBuffer; - const char *query_title = cfg.query->ids()[hits.query]; - output_format->print_query_intro(hits.query, query_title, cfg.query->source_len((unsigned)hits.query), *buf, true, cfg); - output_format->print_query_epilog(*buf, query_title, true, cfg); + Output::Info info{ cfg.query->seq_info(hits.query), true, cfg.db.get(), *buf, {} }; + cfg.output_format->print_query_intro(info); + cfg.output_format->print_query_epilog(info); } return buf; } - QueryMapper *mapper = new ExtensionPipeline::BandedSwipe::Pipeline(hits.query, hits.begin, hits.end, dp_stat, cfg, hits.target_parallel); + QueryMapper *mapper = new ExtensionPipeline::BandedSwipe::Pipeline(hits.query, hits.begin, hits.end, dp_stat, cfg); - task_timer timer("Initializing mapper", hits.target_parallel ? 3 : UINT_MAX); + task_timer timer("Initializing mapper", UINT_MAX); mapper->init(); timer.finish(); - mapper->run(stat); + mapper->run(stat, cfg); timer.go("Generating output"); TextBuffer *buf = nullptr; - if (*output_format != Output_format::null) { + if (*cfg.output_format != OutputFormat::null) { buf = new TextBuffer; - const bool aligned = mapper->generate_output(*buf, stat); + const bool aligned = mapper->generate_output(*buf, stat, cfg); if (aligned && cfg.track_aligned_queries) { query_aligned_mtx.lock(); if (!query_aligned[hits.query]) { @@ -119,98 +220,142 @@ TextBuffer* legacy_pipeline(Align_fetcher &hits, Search::Config& cfg, Statistics return buf; } -void align_worker(size_t thread_id, Search::Config* cfg) +bool align_worker(HitIterator* hit_it, ThreadPool::TaskSet* task_set, Search::Config* cfg) { try { - Align_fetcher hits; + vector hits = **hit_it; +#ifdef OLD + if(hit_it->good(hits)) + task_set->enqueue(align_worker, hit_it, task_set, cfg); +#else + if (!hit_it->good(hits)) + return false; +#endif Statistics stat; DpStat dp_stat; const bool parallel = config.swipe_all && (cfg->target->seqs().size() >= cfg->query->seqs().size()); - while (hits.get()) { + + for (auto h = hits.cbegin(); h < hits.cend(); ++h) { if (config.frame_shift != 0) { - TextBuffer* buf = legacy_pipeline(hits, *cfg, stat); - OutputSink::get().push(hits.query, buf); - hits.release(); + TextBuffer* buf = legacy_pipeline(*h, *cfg, stat); + output_sink->push(h->query, buf); + continue; + } + if (config.pipeline_short) { + TextBuffer* buf = Extension::pipeline_short(h->query, h->begin, h->end, *cfg, stat); + output_sink->push(h->query, buf); continue; } - task_timer timer; - vector matches = Extension::extend(hits.query, hits.begin, hits.end, *cfg, stat, hits.target_parallel || parallel ? DP::Flags::PARALLEL : DP::Flags::NONE); - TextBuffer* buf = blocked_processing ? Extension::generate_intermediate_output(matches, hits.query, *cfg) : Extension::generate_output(matches, hits.query, stat, *cfg); - if (!matches.empty() && cfg->track_aligned_queries) { + if (h->begin == nullptr && !HitIterator::single_query()) { + output_sink->push(h->query, nullptr); + continue; + } + + pair, Extension::Stats> matches = +#ifdef WITH_DNA + align_mode.mode == Align_mode::blastn ? WaveExtension::extend(*cfg, h->query) : +#endif + Extension::extend(h->query, h->begin, h->end, *cfg, stat, parallel ? DP::Flags::PARALLEL : DP::Flags::NONE); + TextBuffer* buf = cfg->blocked_processing ? Extension::generate_intermediate_output(matches.first, h->query, *cfg) : Extension::generate_output(matches.first, matches.second, h->query, stat, *cfg); + if (!matches.first.empty() && cfg->track_aligned_queries) { std::lock_guard lock(query_aligned_mtx); - if (!query_aligned[hits.query]) { - query_aligned[hits.query] = true; + if (!query_aligned[h->query]) { + query_aligned[h->query] = true; ++cfg->iteration_query_aligned; } } - OutputSink::get().push(hits.query, buf); - if (hits.target_parallel) - stat.inc(Statistics::TIME_TARGET_PARALLEL, timer.microseconds()); - hits.release(); - if (config.swipe_all && !config.no_heartbeat && (hits.query % 100 == 0)) - log_stream << "Queries = " << hits.query << std::endl; + if (!config.unaligned_targets.empty()) { + lock_guard lock(cfg->aligned_targets_mtx); + for (const Extension::Match &m : matches.first) { + const OId oid = cfg->target->block_id2oid(m.target_block_id); + if (!cfg->aligned_targets[oid]) + cfg->aligned_targets[oid] = true; + } + } + output_sink->push(h->query, buf); + if (config.swipe_all && !config.no_heartbeat && (h->query % 100 == 0)) + log_stream << "Queries = " << h->query << std::endl; + } + statistics += stat; ::dp_stat += dp_stat; } catch (std::exception& e) { exit_with_error(e); } + return true; } void align_queries(Consumer* output_file, Search::Config& cfg) { - - size_t max_size = std::min(size_t(config.chunk_size*1e9 * 10 * 2) / cfg.index_chunks / 3, config.trace_pt_fetch_size); - if (config.memory_limit != 0.0) - max_size = std::max(max_size, size_t(config.memory_limit * 1e9)); - pair query_range; - if (Stats::CBS::avg_matrix(config.comp_based_stats)) { - Extension::target_matrices.insert(Extension::target_matrices.end(), cfg.target->seqs().size(), nullptr); - Extension::target_matrix_count = 0; - } + const int64_t mem_limit = Util::String::interpret_number(config.memory_limit.get("16G")); + pair query_range; task_timer timer(nullptr, 3); - if (!blocked_processing && !cfg.iterated()) - cfg.db->init_random_access(current_query_chunk, 0, false); - - cfg.seed_hit_buf->load(max_size); + if (!cfg.blocked_processing && !cfg.iterated()) + cfg.db->init_random_access(cfg.current_query_block, 0, false); + + int64_t res_size = cfg.query->mem_size() + cfg.target->mem_size(), last_size = 0; + cfg.seed_hit_buf->load(std::min(mem_limit - res_size - cfg.seed_hit_buf->bin_size(1) * (int64_t)sizeof(Search::Hit), config.trace_pt_fetch_size)); while (true) { timer.go("Loading trace points"); - tuple*, size_t, size_t> input = cfg.seed_hit_buf->retrieve(); + tuple*, BlockId, BlockId> input = cfg.seed_hit_buf->retrieve(); if (get<0>(input) == nullptr) break; statistics.inc(Statistics::TIME_LOAD_SEED_HITS, timer.microseconds()); vector* hit_buf = get<0>(input); + res_size += hit_buf->size() * sizeof(Search::Hit); query_range = { get<1>(input), get<2>(input) }; - cfg.seed_hit_buf->load(max_size); + cfg.seed_hit_buf->load(std::min(mem_limit - res_size, config.trace_pt_fetch_size)); + + if (res_size + last_size > mem_limit) + message_stream << "Warning: resident size (" << (res_size + last_size) << ") exceeds memory limit." << std::endl; timer.go("Sorting trace points"); -#if _MSC_FULL_VER == 191627045 - radix_sort(hit_buf->data(), hit_buf->data() + hit_buf->size(), (uint32_t)query_range.second * align_mode.query_contexts, config.threads_); -#else - ips4o::parallel::sort(hit_buf->data(), hit_buf->data() + hit_buf->size(), std::less(), config.threads_); -#endif + ips4o::parallel::sort(hit_buf->data(), hit_buf->data() + hit_buf->size(), std::less<>(), config.threads_); statistics.inc(Statistics::TIME_SORT_SEED_HITS, timer.microseconds()); +#ifndef OLD + timer.go("Computing partition"); + make_partition(hit_buf->data(), hit_buf->data() + hit_buf->size()); +#endif + timer.go("Computing alignments"); - Align_fetcher::init(query_range.first, query_range.second, hit_buf->data(), hit_buf->data() + hit_buf->size()); - OutputSink::instance = unique_ptr(new OutputSink(query_range.first, output_file)); - vector threads; + HitIterator hit_it(query_range.first, query_range.second, hit_buf->data(), hit_buf->data() + hit_buf->size()); + OutputWriter writer{ output_file }; + output_sink.reset(new ReorderQueue(query_range.first, writer)); + unique_ptr heartbeat; if (config.verbosity >= 3 && config.load_balancing == Config::query_parallel && !config.no_heartbeat && !config.swipe_all) - threads.emplace_back(heartbeat_worker, query_range.second, &cfg); + heartbeat.reset(new thread(heartbeat_worker, query_range.second, &cfg)); size_t n_threads = config.threads_align == 0 ? config.threads_ : config.threads_align; if (config.load_balancing == Config::target_parallel || (config.swipe_all && (cfg.target->seqs().size() >= cfg.query->seqs().size()))) n_threads = 1; - for (size_t i = 0; i < n_threads; ++i) - threads.emplace_back(align_worker, i, &cfg); - for (auto &t : threads) - t.join(); + auto task = [&hit_it, &cfg](ThreadPool& tp) { + return align_worker(&hit_it, nullptr, &cfg); + }; +#ifndef OLD + cfg.thread_pool.reset(new ThreadPool(task)); + cfg.thread_pool->run(n_threads, !config.no_heartbeat); + cfg.thread_pool->join(); +#else + cfg.thread_pool.reset(new ThreadPool ()); + cfg.thread_pool->run(n_threads); + ThreadPool::TaskSet task_set(*cfg.thread_pool, 1); + task_set.enqueue(align_worker, &hit_it, &task_set, &cfg); + task_set.wait(); +#endif + if (heartbeat) + heartbeat->join(); statistics.inc(Statistics::TIME_EXT, timer.microseconds()); timer.go("Deallocating buffers"); + cfg.thread_pool.reset(); + output_sink.reset(); + last_size = hit_buf->size() * sizeof(Search::Hit); + res_size -= last_size; delete hit_buf; } statistics.max(Statistics::SEARCH_TEMP_SPACE, cfg.seed_hit_buf->total_disk_size()); @@ -219,6 +364,6 @@ void align_queries(Consumer* output_file, Search::Config& cfg) Extension::target_matrices.clear(); statistics.inc(Statistics::MATRIX_ADJUST_COUNT, Extension::target_matrix_count); - if (!blocked_processing && !cfg.iterated()) + if (!cfg.blocked_processing && !cfg.iterated()) cfg.db->end_random_access(false); } \ No newline at end of file diff --git a/src/align/align.h b/src/align/align.h index 17814ca61..0e8beb59e 100644 --- a/src/align/align.h +++ b/src/align/align.h @@ -24,7 +24,6 @@ along with this program. If not, see . #include #include #include -#include "../util/task_queue.h" #include "../basic/statistics.h" #include "legacy/query_mapper.h" #include "../run/workflow.h" @@ -53,21 +52,22 @@ namespace ExtensionPipeline { Pipeline(size_t query_id, Search::Hit* begin, Search::Hit* end, const Search::Config &cfg) : QueryMapper(query_id, begin, end, cfg) {} - virtual void run(Statistics &stat) override; + virtual void run(Statistics &stat, const Search::Config& cfg) override; + virtual ~Pipeline() {} }; } namespace BandedSwipe { struct Target; struct Pipeline : public QueryMapper { - Pipeline(size_t query_id, Search::Hit* begin, Search::Hit* end, DpStat &dp_stat, const Search::Config &cfg, bool target_parallel) : - QueryMapper(query_id, begin, end, cfg, target_parallel), + Pipeline(size_t query_id, Search::Hit* begin, Search::Hit* end, DpStat &dp_stat, const Search::Config &cfg) : + QueryMapper(query_id, begin, end, cfg), dp_stat(dp_stat) {} Target& target(size_t i); - virtual void run(Statistics &stat) override; + virtual void run(Statistics &stat, const Search::Config& cfg) override; void run_swipe(bool score_only); - void range_ranking(); + void range_ranking(const int64_t max_target_seqs); DpStat &dp_stat; }; } diff --git a/src/align/alt_hsp.cpp b/src/align/alt_hsp.cpp index 255b6ef8d..55dae4eb5 100644 --- a/src/align/alt_hsp.cpp +++ b/src/align/alt_hsp.cpp @@ -4,6 +4,7 @@ using std::list; using std::array; +using std::vector; namespace Extension { @@ -67,19 +68,19 @@ static TargetVec recompute_alt_hsps(const Sequence* query_seq, const int query_s const Loc qlen = query_seq[0].length(); for (auto it = targets.begin(); it != targets.end(); ++it) { const int64_t dp_size = (int64_t)qlen * (int64_t)it->match->seq.length(); - const Stats::TargetMatrix* matrix = it->match->matrix.blank() ? nullptr : &it->match->matrix; + const ::Stats::TargetMatrix* matrix = it->match->matrix.blank() ? nullptr : &it->match->matrix; const int bin = DP::BandedSwipe::bin(v, qlen, 0, 0, dp_size, matrix ? matrix->score_width() : 0, 0); for (int32_t context = 0; context < align_mode.query_contexts; ++context) { if (it->masked_seq[context]) { const Sequence target = it->masked(context); - dp_targets[context][bin].emplace_back(target, target.length(), it - targets.begin(), matrix); + dp_targets[context][bin].emplace_back(target, target.length(), BlockId(it - targets.begin()), matrix); } } } for (int32_t context = 0; context < align_mode.query_contexts; ++context) { - const int8_t* cbs = Stats::CBS::hauser(config.comp_based_stats) ? query_cb[context].int8.data() : nullptr; - DP::Params params{ query_seq[context], Frame(context), query_source_len, cbs, DP::Flags::FULL_MATRIX, v, stats }; + const int8_t* cbs = ::Stats::CBS::hauser(config.comp_based_stats) ? query_cb[context].int8.data() : nullptr; + DP::Params params{ query_seq[context], "", Frame(context), query_source_len, cbs, DP::Flags::FULL_MATRIX, v, stats, nullptr }; list hsp = DP::BandedSwipe::swipe(dp_targets[context], params); while (!hsp.empty()) { ActiveTarget& t = targets[hsp.front().swipe_target]; @@ -101,13 +102,13 @@ static TargetVec recompute_alt_hsps(const Sequence* query_seq, const int query_s return out; } -void recompute_alt_hsps(vector& matches, const Sequence* query, const int query_source_len, const Bias_correction* query_cb, const HspValues v, Statistics& stats) { +void recompute_alt_hsps(vector::iterator begin, vector::iterator end, const Sequence* query, const int query_source_len, const Bias_correction* query_cb, const HspValues v, Statistics& stats) { if (config.max_hsps == 1) return; TargetVec targets; - targets.reserve(matches.size()); + targets.reserve(end - begin); SequenceSet target_seqs; - for (auto i = matches.begin(); i != matches.end(); ++i) + for (auto i = begin; i != end; ++i) targets.emplace_back(i, target_seqs); target_seqs.finish_reserve(); int64_t i = 0; diff --git a/src/align/culling.cpp b/src/align/culling.cpp index 845c30e8c..5606ff352 100644 --- a/src/align/culling.cpp +++ b/src/align/culling.cpp @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -22,6 +22,9 @@ along with this program. If not, see . #include "target.h" #include "../basic/config.h" #include "../data/reference.h" +#include "../output/recursive_parser.h" +#include "../output/output_format.h" +#include "culling.h" using std::vector; using std::list; @@ -60,7 +63,7 @@ void Target::inner_culling() { hsp[i].resize(1); } else - hsp[i].clear(); + hsp[i].clear(); return; } list hsps; @@ -86,15 +89,38 @@ void Match::max_hsp_culling() { Extension::max_hsp_culling(hsp); } -bool append_hits(vector& targets, vector::const_iterator begin, vector::const_iterator end, size_t chunk_size, int source_query_len, const char* query_title, const Sequence& query_seq, const Block& target_block) { - bool append = false; +static void sort_targets(vector& targets) { + std::sort(targets.begin(), targets.end(), config.toppercent < 100.0 ? Target::comp_score : Target::comp_evalue); +} - if (config.toppercent != 100.0 || targets.size() >= config.max_alignments) { - culling(targets, source_query_len, query_title, query_seq, 0, target_block); +template +static It output_range(const It begin, const It end, const Search::Config& cfg) { + if (end <= begin) + return begin; + It i = begin; + if (i->filter_evalue == DBL_MAX) + return begin; + if (config.toppercent < 100.0) { + const double cutoff = std::max(top_cutoff_score(score_matrix.bitscore(begin->filter_score)), 1.0); + while (i < end && (score_matrix.bitscore(i->filter_score) >= cutoff)) + ++i; } - else - append = true; + else { + i += std::min(cfg.max_target_seqs, end - begin); + while (--i > begin && i->filter_evalue == DBL_MAX); + ++i; + } + return i; +} +bool append_hits(vector& targets, vector::iterator begin, vector::iterator end, bool with_culling, const Search::Config& cfg) { + if (end <= begin) + return false; + bool new_hits = config.toppercent == 100.0 && (int64_t)targets.size() < cfg.max_target_seqs; + bool append = !with_culling || new_hits; + + culling(targets, append, cfg); + int max_score = 0; double min_evalue = DBL_MAX; for (auto i = begin; i < end; ++i) { @@ -102,61 +128,50 @@ bool append_hits(vector& targets, vector::const_iterator begin, min_evalue = std::min(min_evalue, i->filter_evalue); } - if (targets.empty()) - append = true; - else if (config.toppercent == 100.0 && min_evalue <= targets.back().filter_evalue) - append = true; - else if (config.toppercent != 100.0 && max_score >= top_cutoff_score(targets.front().filter_score)) - append = true; - - /*if (max_score <= low_score && previous_count >= config.max_alignments && config.toppercent == 100.0) - return false;*/ + vector::const_iterator range_end = output_range(targets.begin(), targets.end(), cfg); - /*if (config.toppercent == 100.0 && (config.min_id > 0 || config.query_cover > 0 || config.subject_cover > 0 || config.no_self_hits)) - return true;*/ + if (targets.empty() + || (config.toppercent == 100.0 && min_evalue <= (range_end - 1)->filter_evalue) + || (config.toppercent != 100.0 && max_score >= top_cutoff_score((range_end - 1)->filter_score))) { + append = true; + new_hits = true; + } if(append) - targets.insert(targets.end(), begin, end); + targets.insert(targets.end(), std::make_move_iterator(begin), std::make_move_iterator(end)); - return append; + return new_hits; } -bool filter_hsp(const Hsp& hsp, int source_query_len, const char *query_title, int subject_len, const char* subject_title, const Sequence& query_seq, const Sequence& subject_seq) { - return hsp.id_percent() < config.min_id - || hsp.query_cover_percent(source_query_len) < config.query_cover - || hsp.subject_cover_percent(subject_len) < config.subject_cover +bool filter_hsp(Hsp& hsp, int source_query_len, const char *query_title, int subject_len, const char* subject_title, const Sequence& query_seq, const Sequence& subject_seq, const double query_self_aln_score, const double target_self_aln_score, const OutputFormat* output_format) { + bool cluster_threshold = true; + if (config.cluster_threshold.present()) { + HspContext context(hsp, 0, 0, TranslatedSequence(query_seq), query_title, 0, subject_len, subject_title, 0, 0, subject_seq, 0, query_self_aln_score, target_self_aln_score); + RecursiveParser rp(&context, dynamic_cast(output_format)->format.c_str()); + cluster_threshold = rp.evaluate() >= config.cluster_threshold; + } + const double qcov = hsp.query_cover_percent(source_query_len), + tcov = hsp.subject_cover_percent(subject_len), + approx_min_id = config.approx_min_id.get(0.0); + return !cluster_threshold + || hsp.id_percent() < config.min_id + || (approx_min_id > 0 && hsp.approx_id < approx_min_id) + || qcov < config.query_cover + || tcov < config.subject_cover + || (qcov < config.query_or_target_cover && tcov < config.query_or_target_cover) || (config.no_self_hits && query_seq == subject_seq && strcmp(query_title, subject_title) == 0); } -void Target::apply_filters(int source_query_len, const char *query_title, const Sequence& query_seq, const Block& targets) -{ - const char* title = config.no_self_hits ? targets.ids()[block_id] : nullptr; - const Sequence seq = targets.seqs()[block_id]; - const int len = seq.length(); - filter_score = 0; - filter_evalue = DBL_MAX; - for (int frame = 0; frame < align_mode.query_contexts; ++frame) { - for (list::iterator i = hsp[frame].begin(); i != hsp[frame].end();) { - if (filter_hsp(*i, source_query_len, query_title, len, title, query_seq, seq)) - i = hsp[frame].erase(i); - else { - filter_score = std::max(filter_score, i->score); - filter_evalue = std::min(filter_evalue, i->evalue); - ++i; - } - } - } -} - -void Match::apply_filters(int source_query_len, const char *query_title, const Sequence& query_seq, const Block& targets) +void Match::apply_filters(int source_query_len, const char *query_title, const Sequence& query_seq, const double query_self_aln_score, const Block& targets, const OutputFormat* output_format) { const char* title = config.no_self_hits ? targets.ids()[target_block_id] : nullptr; const Sequence seq = targets.seqs()[target_block_id]; const int len = seq.length(); + const double self_aln = targets.has_self_aln() ? targets.self_aln_score(target_block_id) : 0.0; for (list::iterator i = hsp.begin(); i != hsp.end();) { - if (filter_hsp(*i, source_query_len, query_title, len, title, query_seq, seq)) + if (filter_hsp(*i, source_query_len, query_title, len, title, query_seq, seq, query_self_aln_score, self_aln, output_format)) i = hsp.erase(i); else ++i; @@ -165,32 +180,21 @@ void Match::apply_filters(int source_query_len, const char *query_title, const S filter_score = hsp.empty() ? 0 : hsp.front().score; } -void culling(std::vector& targets, int source_query_len, const char* query_title, const Sequence& query_seq, size_t min_keep, const Block& target_block) { - if (config.min_id > 0 || config.query_cover > 0 || config.subject_cover > 0 || config.no_self_hits) - for (Target& match : targets) - match.apply_filters(source_query_len, query_title, query_seq, target_block); +void culling(std::vector& targets, bool sort_only, const Search::Config& cfg) { + sort_targets(targets); + if (!sort_only) + targets.erase(output_range(targets.begin(), targets.end(), cfg), targets.end()); +} - std::sort(targets.begin(), targets.end(), config.toppercent < 100.0 ? Target::comp_score : Target::comp_evalue); - if (targets.empty() || targets.front().filter_evalue == DBL_MAX) { - targets.clear(); - return; - } +void apply_filters(std::vector::iterator begin, std::vector::iterator end, int source_query_len, const char* query_title, const double query_self_aln_score, const Sequence& query_seq, const Search::Config& cfg) { + if (config.min_id > 0 || config.approx_min_id.get(0.0) > 0 || config.query_cover > 0 || config.subject_cover > 0 || config.query_or_target_cover > 0 || config.no_self_hits || config.cluster_threshold.present()) + for (auto i = begin; i < end; ++i) + i->apply_filters(source_query_len, query_title, query_seq, query_self_aln_score, *cfg.target, cfg.output_format.get()); +} - std::vector::iterator i = targets.begin(); - if (config.toppercent < 100.0) { - size_t n = 0; - const double cutoff = std::max(top_cutoff_score(score_matrix.bitscore(targets.front().filter_score)), 1.0); - while (i < targets.end() && (score_matrix.bitscore(i->filter_score) >= cutoff || n < min_keep)) { - ++i; - ++n; - } - } - else { - i += std::min(std::max(config.max_alignments, min_keep), targets.size()); - while (--i > targets.begin() && i->filter_evalue == DBL_MAX); - ++i; - } - targets.erase(i, targets.end()); +void culling(std::vector& targets, const Search::Config& cfg) { + std::sort(targets.begin(), targets.end(), config.toppercent < 100.0 ? Match::cmp_score : Match::cmp_evalue); + targets.erase(output_range(targets.begin(), targets.end(), cfg), targets.end()); } } \ No newline at end of file diff --git a/src/align/culling.h b/src/align/culling.h new file mode 100644 index 000000000..50eaa3ec7 --- /dev/null +++ b/src/align/culling.h @@ -0,0 +1,18 @@ +#pragma once +#include "../basic/match.h" +#include "../output/output_format.h" + +namespace Extension { + +bool filter_hsp(Hsp& hsp, + int source_query_len, + const char *query_title, + int subject_len, + const char* subject_title, + const Sequence& query_seq, + const Sequence& subject_seq, + const double query_self_aln_score, + const double target_self_aln_score, + const OutputFormat* output_format); + +} \ No newline at end of file diff --git a/src/align/def.h b/src/align/def.h new file mode 100644 index 000000000..663696dd8 --- /dev/null +++ b/src/align/def.h @@ -0,0 +1,18 @@ +#pragma once +#include "../dp/flags.h" + +namespace Extension { + +enum class Mode { + BANDED_FAST, BANDED_SLOW, FULL, GLOBAL +}; + +int band(int len, const Mode mode); +HspValues filter_hspvalues(); + +} + +template<> +struct EnumTraits { + static const SEMap from_string; +}; diff --git a/src/align/extend.cpp b/src/align/extend.cpp index aa50dac9e..9569899bd 100644 --- a/src/align/extend.cpp +++ b/src/align/extend.cpp @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2020-2021 Max Planck Society for the Advancement of Science e.V. Code developed by Benjamin Buchfink @@ -22,6 +22,7 @@ along with this program. If not, see . #include #include #include +#include #include "extend.h" #include "../data/queries.h" #include "../basic/config.h" @@ -36,13 +37,20 @@ along with this program. If not, see . #include "../masking/masking.h" #include "../search/hit.h" #include "load_hits.h" +#include "def.h" +using std::accumulate; using std::vector; using std::list; using std::array; using std::pair; using std::endl; using std::move; +using std::make_move_iterator; +using std::any_of; +using std::numeric_limits; +using std::tie; +using std::min; const SEMap EnumTraits::from_string = { { "banded-fast", Extension::Mode::BANDED_FAST}, @@ -53,7 +61,10 @@ const SEMap EnumTraits::from_string = { namespace Extension { +#include "extend_chunk.h" + const std::map default_ext_mode = { + { Sensitivity::FASTER, Mode::BANDED_FAST}, { Sensitivity::FAST, Mode::BANDED_FAST}, { Sensitivity::DEFAULT, Mode::BANDED_FAST}, { Sensitivity::MID_SENSITIVE, Mode::BANDED_FAST}, @@ -63,40 +74,28 @@ const std::map default_ext_mode = { { Sensitivity::ULTRA_SENSITIVE, Mode::BANDED_SLOW} }; -constexpr size_t MAX_CHUNK_SIZE = 400, MIN_CHUNK_SIZE = 128; +constexpr int64_t MAX_CHUNK_SIZE = 400, MIN_CHUNK_SIZE = 128, MAPANY_CHUNK_SIZE = 16; -size_t ranking_chunk_size(size_t target_count, size_t ref_letters) { +int64_t ranking_chunk_size(int64_t target_count, const int64_t ref_letters, const int64_t max_target_seqs) { if (config.no_ranking || config.global_ranking_targets > 0) return target_count; if (config.ext_chunk_size > 0) return config.ext_chunk_size; + if (config.mapany) + return MAPANY_CHUNK_SIZE; const double default_letters = config.sensitivity >= Sensitivity::VERY_SENSITIVE ? 800 * 1e6 : 2 * 1e9; - const size_t block_mult = std::max(size_t(std::round((double)ref_letters / default_letters)), (size_t)1); + const int64_t block_mult = std::max(int64_t(std::round((double)ref_letters / default_letters)), (int64_t)1); if (config.toppercent < 100.0) return MIN_CHUNK_SIZE * block_mult; - return std::max(MIN_CHUNK_SIZE, std::min(make_multiple(config.max_alignments, (size_t)32), MAX_CHUNK_SIZE)) * block_mult; -} - -size_t chunk_size_multiplier(const FlatArray& seed_hits, int query_len) { - return seed_hits.size() * query_len / seed_hits.data_size() < config.seedhit_density ? config.chunk_size_multiplier : 1; + const int64_t size = std::max(MIN_CHUNK_SIZE, std::min(make_multiple(max_target_seqs, (int64_t)32), MAX_CHUNK_SIZE)) * block_mult; + return config.target_hard_cap ? std::min(size, config.target_hard_cap) : size; } -static size_t lazy_masking(const vector& target_block_ids, Block& targets, const MaskingAlgo algo) { - if (algo == MaskingAlgo::NONE) - return 0; - vector seq; - const Masking& masking = Masking::get(); - size_t n = 0; - for (uint32_t t : target_block_ids) - if (targets.fetch_seq_if_unmasked(t, seq)) { - masking(seq.data(), seq.size(), algo, t); - targets.write_masked_seq(t, seq); - ++n; - } - return n; +static bool have_filters(const Search::Config& cfg) { + return config.min_id > 0 || config.approx_min_id.get(0.0) > 0 || config.query_cover > 0 || config.subject_cover > 0 || config.query_or_target_cover > 0; } -static HspValues first_round_hspv() { +static HspValues first_round_hspv(const Search::Config& cfg) { HspValues first_round = HspValues::NONE; if (config.min_id > 0) first_round |= HspValues::IDENT | HspValues::LENGTH; @@ -104,52 +103,43 @@ static HspValues first_round_hspv() { first_round |= HspValues::QUERY_COORDS; if (config.subject_cover > 0) first_round |= HspValues::TARGET_COORDS; + if (config.cluster_threshold.present()) + first_round |= cfg.output_format->hsp_values; return first_round; } -vector extend(size_t query_id, - const Sequence *query_seq, - int source_query_len, - const Bias_correction *query_cb, - const Stats::Composition& query_comp, - FlatArray &seed_hits, - vector &target_block_ids, - const Search::Config& cfg, - Statistics& stat, - DP::Flags flags, - const HspValues hsp_values) -{ - static const Loc GAPPED_FILTER_MIN_QLEN = 85; - stat.inc(Statistics::TARGET_HITS2, target_block_ids.size()); - task_timer timer(flag_any(flags, DP::Flags::PARALLEL) ? config.target_parallel_verbosity : UINT_MAX); - - if (cfg.lazy_masking && !config.global_ranking_targets) - stat.inc(Statistics::MASKED_LAZY, lazy_masking(target_block_ids, *cfg.target, cfg.target_masking)); - - if (cfg.gapped_filter_evalue > 0.0 && config.global_ranking_targets == 0 && (!align_mode.query_translated || query_seq[0].length() >= GAPPED_FILTER_MIN_QLEN)) { - timer.go("Computing gapped filter"); - gapped_filter(query_seq, query_cb, seed_hits, target_block_ids, stat, flags, cfg); - if (!flag_any(flags, DP::Flags::PARALLEL)) - stat.inc(Statistics::TIME_GAPPED_FILTER, timer.microseconds()); - } - stat.inc(Statistics::TARGET_HITS3, target_block_ids.size()); +static bool ranking_terminate(bool new_hits, int last_tail_score, int tail_score, int64_t targets_processed, int64_t targets_aligned) { + if (config.target_hard_cap && targets_processed >= config.target_hard_cap) + return true; + if (config.mapany && config.toppercent == 100.0 && targets_aligned > 0) + return true; + return !new_hits && (last_tail_score == 0 + || double(tail_score) / (double)last_tail_score <= config.ranking_score_drop_factor + || score_matrix.bitscore(tail_score) < config.ranking_cutoff_bitscore); +} - timer.go("Computing chaining"); - vector targets = ungapped_stage(query_seq, query_cb, query_comp, seed_hits, target_block_ids, flags, stat, *cfg.target, cfg.extension_mode); - if (!flag_any(flags, DP::Flags::PARALLEL)) - stat.inc(Statistics::TIME_CHAINING, timer.microseconds()); +Match Match::self_match(BlockId query_id, Sequence query_seq) { + Match m(query_id, query_seq, ::Stats::TargetMatrix(), 0, numeric_limits::max(), 0.0); + m.hsp.emplace_back(); + m.hsp.back().evalue = 0.0; + m.hsp.back().score = numeric_limits::max(); + m.hsp.back().bit_score = DBL_MAX; + m.hsp.back().query_range = { 0,query_seq.length() }; + m.hsp.back().query_source_range = { 0,query_seq.length() }; + m.hsp.back().subject_range = { 0,query_seq.length() }; + return m; +} - return align(targets, query_seq, query_cb, source_query_len, flags, hsp_values, cfg.extension_mode, stat); +static bool add_self_aln(const Search::Config& cfg) { + return config.add_self_aln && ((config.self && cfg.current_ref_block == 0) || (!config.self && cfg.current_query_block == cfg.current_ref_block)); } -vector extend( - size_t query_id, +pair, Stats> extend( + BlockId query_id, const Search::Config& cfg, Statistics& stat, DP::Flags flags, - FlatArray& seed_hits, - vector& target_block_ids, - const vector& target_scores) + SeedHitList& l) { const unsigned UNIFIED_TARGET_LEN = 50; const unsigned contexts = align_mode.query_contexts; @@ -158,127 +148,147 @@ vector extend( const char* query_title = cfg.query->ids()[query_id]; if (config.log_query || (flag_any(flags, DP::Flags::PARALLEL) && !config.swipe_all)) - log_stream << "Query=" << query_title << " Hits=" << seed_hits.data_size() << endl; + log_stream << "Query=" << query_title << " Hits=" << l.seed_hits.data_size() << endl; for (unsigned i = 0; i < contexts; ++i) query_seq.push_back(cfg.query->seqs()[query_id * contexts + i]); const unsigned query_len = (unsigned)query_seq.front().length(); - task_timer timer(flag_any(flags, DP::Flags::PARALLEL) ? config.target_parallel_verbosity : UINT_MAX); - if (Stats::CBS::hauser(config.comp_based_stats)) { - timer.go("Computing CBS"); + if (::Stats::CBS::hauser(config.comp_based_stats)) { for (unsigned i = 0; i < contexts; ++i) query_cb.emplace_back(query_seq[i]); - timer.finish(); } - Stats::Composition query_comp; - if (Stats::CBS::matrix_adjust(config.comp_based_stats)) - query_comp = Stats::composition(query_seq[0]); + ::Stats::Composition query_comp; + if (::Stats::CBS::matrix_adjust(config.comp_based_stats)) + query_comp = ::Stats::composition(query_seq[0]); const int source_query_len = align_mode.query_translated ? (int)cfg.query->source_seqs()[query_id].length() : (int)cfg.query->seqs()[query_id].length(); - const size_t target_count = target_block_ids.size(); - const size_t chunk_size = ranking_chunk_size(target_count, cfg.target->seqs().letters()); - vector::const_iterator i0 = target_scores.cbegin(), i1 = i0 + std::min((ptrdiff_t)chunk_size, target_scores.cend() - i0); + const double self_aln_score = cfg.query->has_self_aln() ? cfg.query->self_aln_score(query_id) : 0.0; + const size_t target_count = l.target_block_ids.size(); + const int64_t chunk_size = ranking_chunk_size(target_count, cfg.target->seqs().letters(), cfg.max_target_seqs); + vector::const_iterator i0 = l.target_scores.cbegin(), i1 = i0 + std::min((ptrdiff_t)chunk_size, l.target_scores.cend() - i0); - if (config.toppercent == 100.0 && config.min_bit_score == 0.0) + if (config.toppercent == 100.0 && config.min_bit_score == 0.0 && (i1 - i0) < cfg.max_target_seqs && (config.ext_chunk_size == 0 || config.lin_stage1)) #ifdef EVAL_TARGET - while (i1 < target_scores.cend() && i1->evalue <= config.max_evalue && size_t(i1 - i0) < config.max_alignments) ++i1; + while (i1 < l.target_scores.cend() && i1->evalue <= config.max_evalue && size_t(i1 - i0) < config.max_alignments) ++i1; #else - while (i1 < target_scores.cend() && score_matrix.evalue(i1->score, query_len, UNIFIED_TARGET_LEN) <= config.max_evalue) i1 += 16; + while (i1 < l.target_scores.cend() && score_matrix.evalue(i1->score, query_len, UNIFIED_TARGET_LEN) <= config.max_evalue) i1 += min((ptrdiff_t)16, l.target_scores.cend() - i1); #endif - - const int low_score = config.query_memory ? memory->low_score(query_id) : 0; - const size_t previous_count = config.query_memory ? memory->count(query_id) : 0; - - - const HspValues first_round = first_round_hspv(); - - //size_t multiplier = 1; - int tail_score = 0; - thread_local FlatArray seed_hits_chunk; - thread_local vector target_block_ids_chunk; - - vector aligned_targets; - while (i0 < target_scores.cend()) { - seed_hits_chunk.clear(); - target_block_ids_chunk.clear(); - const size_t current_chunk_size = (size_t)(i1 - i0); - const bool multi_chunk = current_chunk_size < target_scores.size(); - if (config.query_memory && memory->ranking_failed_count(query_id) >= chunk_size && memory->ranking_low_score(query_id) >= i0->score) - break; - - if (multi_chunk) - for (vector::const_iterator j = i0; j < i1; ++j) { - target_block_ids_chunk.push_back(target_block_ids[j->target]); - seed_hits_chunk.push_back(seed_hits.begin(j->target), seed_hits.end(j->target)); + + const HspValues first_round_hspv = (config.prefix_scan || config.anchored_swipe) ? HspValues::COORDS : HspValues::NONE; + const bool first_round_culling = !have_filters(cfg) || config.toppercent != 100.0; + bool new_hits_ev = false; + int tail_score = 0, previous_tail_score = 0; + FlatArray seed_hits_chunk; + vector target_block_ids_chunk; + vector matches; + Stats stats; + + do { + + vector aligned_targets; + bool new_hits; + + do { + const int64_t current_chunk_size = i1 - i0; + const bool multi_chunk = current_chunk_size < (int64_t)l.target_scores.size(); + + if (multi_chunk) { + target_block_ids_chunk.clear(); + seed_hits_chunk.clear(); + target_block_ids_chunk.reserve(i1 - i0); + seed_hits_chunk.reserve(i1 - i0, accumulate(i0, i1, (int64_t)0, [&l](int64_t n, const TargetScore& s) { return l.seed_hits.count(s.target) + n; })); + for (vector::const_iterator j = i0; j < i1; ++j) { + target_block_ids_chunk.push_back(l.target_block_ids[j->target]); + seed_hits_chunk.push_back(l.seed_hits.begin(j->target), l.seed_hits.end(j->target)); + } } - //multiplier = std::max(multiplier, chunk_size_multiplier(seed_hits_chunk, (int)query_seq.front().length())); - - vector v = extend(query_id, query_seq.data(), source_query_len, query_cb.data(), query_comp, multi_chunk ? seed_hits_chunk : seed_hits, multi_chunk ? target_block_ids_chunk : target_block_ids, cfg, stat, flags, first_round); - const size_t n = v.size(); - stat.inc(Statistics::TARGET_HITS4, v.size()); - bool new_hits = false; - if (multi_chunk) - new_hits = append_hits(aligned_targets, v.begin(), v.end(), chunk_size, source_query_len, query_title, query_seq.front(), *cfg.target); - else - aligned_targets = move(v); - - if (n == 0 || !new_hits) { - if (config.query_memory && current_chunk_size >= chunk_size) - memory->update_failed_count(query_id, current_chunk_size, (i1 - 1)->score); - const double tail_bit_score = score_matrix.bitscore((i1 - 1)->score); - if (tail_score == 0 || double((i1 - 1)->score) / (double)tail_score <= config.ranking_score_drop_factor || tail_bit_score < config.ranking_cutoff_bitscore) - break; - } - else - tail_score = (i1 - 1)->score; - - i0 = i1; - i1 = std::min(i1 + std::min(chunk_size, MAX_CHUNK_SIZE), target_scores.cend()); - } - - if (config.swipe_all) - aligned_targets = full_db_align(query_seq.data(), query_cb.data(), flags, first_round, stat, *cfg.target); - - /*if (multiplier > 1) - stat.inc(Statistics::HARD_QUERIES);*/ - - timer.go("Computing culling"); - culling(aligned_targets, source_query_len, query_title, query_seq.front(), 0, *cfg.target); - if (config.query_memory) - memory->update(query_id, aligned_targets.begin(), aligned_targets.end()); - stat.inc(Statistics::TARGET_HITS5, aligned_targets.size()); - timer.finish(); - - vector matches = align(aligned_targets, query_seq.data(), query_cb.data(), source_query_len, flags, first_round, cfg.extension_mode, stat); - std::sort(matches.begin(), matches.end(), config.toppercent == 100.0 ? Match::cmp_evalue : Match::cmp_score); - return matches; + pair, Stats> v = extend( + query_id, + query_seq.data(), + source_query_len, + query_cb.data(), + query_comp, + multi_chunk ? seed_hits_chunk.begin() : l.seed_hits.begin(), + multi_chunk ? seed_hits_chunk.end() : l.seed_hits.end(), + multi_chunk ? target_block_ids_chunk.cbegin() : l.target_block_ids.cbegin(), + cfg, + stat, + flags, + HspValues::NONE); + + stats += v.second; + stat.inc(Statistics::TARGET_HITS4, v.first.size()); + new_hits = new_hits_ev = v.first.size() > 0; + if (multi_chunk) + new_hits = append_hits(aligned_targets, v.first.begin(), v.first.end(), first_round_culling, cfg); + else + aligned_targets = move(v.first); + + i0 = i1; + i1 += std::min(std::min(chunk_size, MAX_CHUNK_SIZE), l.target_scores.cend() - i1); + previous_tail_score = tail_score; + if (new_hits) + tail_score = (i1 - 1)->score; + } while (i0 < l.target_scores.cend() && !ranking_terminate(new_hits, previous_tail_score, (i1 - 1)->score, i1 - l.target_scores.cbegin(), aligned_targets.size())); + + if (config.swipe_all) + aligned_targets = full_db_align(query_seq.data(), query_cb.data(), flags, HspValues::NONE, stat, *cfg.target); + + culling(aligned_targets, !first_round_culling, cfg); + stat.inc(Statistics::TARGET_HITS5, aligned_targets.size()); + + vector round_matches = align(aligned_targets, matches.size(), query_seq.data(), query_title, query_cb.data(), source_query_len, self_aln_score, flags, first_round_hspv, first_round_culling, stat, cfg); + matches.insert(matches.end(), make_move_iterator(round_matches.begin()), make_move_iterator(round_matches.end())); + } while (config.toppercent == 100.0 && (int64_t)matches.size() < config.max_target_seqs_.get(DEFAULT_MAX_TARGET_SEQS) && i0 < l.target_scores.cend() && new_hits_ev && (!config.mapany || (config.mapany && matches.empty()))); + + if (add_self_aln(cfg) && !any_of(matches.cbegin(), matches.cend(), [query_id](const Match& m) { return m.target_block_id == query_id; })) + matches.push_back(Match::self_match(query_id, query_seq[0])); + + culling(matches, cfg); + + return make_pair(move(matches), stats); } -vector extend(size_t query_id, Search::Hit* begin, Search::Hit* end, const Search::Config &cfg, Statistics &stat, DP::Flags flags) { +pair, Stats> extend(BlockId query_id, Search::Hit* begin, Search::Hit* end, const Search::Config &cfg, Statistics &stat, DP::Flags flags) { task_timer timer(flag_any(flags, DP::Flags::PARALLEL) ? config.target_parallel_verbosity : UINT_MAX); timer.go("Loading seed hits"); - thread_local FlatArray seed_hits; - thread_local vector target_block_ids; - thread_local vector target_scores; - load_hits(begin, end, seed_hits, target_block_ids, target_scores, cfg.target->seqs()); - stat.inc(Statistics::TARGET_HITS0, target_block_ids.size()); + SeedHitList l = load_hits(begin, end, cfg.target->seqs()); + stat.inc(Statistics::TARGET_HITS0, l.target_block_ids.size()); stat.inc(Statistics::TIME_LOAD_HIT_TARGETS, timer.microseconds()); timer.finish(); - const size_t target_count = target_block_ids.size(); - const size_t chunk_size = ranking_chunk_size(target_count, cfg.target->seqs().letters()); + + vector trivial_matches; + if (config.filter_kmer_len) { + tie(l, trivial_matches) = kmer_filter(cfg.query->seqs()[query_id], Bias_correction(cfg.query->seqs()[query_id]).int8.data(), *cfg.target, l); + stat.inc(Statistics::TRIVIAL_ALN, trivial_matches.size()); + } + + const int64_t target_count = (int64_t)l.target_block_ids.size(); + if (target_count == 0 && !config.swipe_all) { + if (add_self_aln(cfg)) + return { {Match::self_match(query_id, cfg.query->seqs()[query_id])}, Stats() }; + culling(trivial_matches, cfg); + return { trivial_matches, Stats() }; + } + const int64_t chunk_size = ranking_chunk_size(target_count, cfg.target->seqs().letters(), cfg.max_target_seqs); if (chunk_size < target_count || config.global_ranking_targets > 0) { timer.go("Sorting targets by score"); - std::sort(target_scores.begin(), target_scores.end()); + std::sort(l.target_scores.begin(), l.target_scores.end()); stat.inc(Statistics::TIME_SORT_TARGETS_BY_SCORE, timer.microseconds()); timer.finish(); if (config.global_ranking_targets > 0) - return GlobalRanking::ranking_list(query_id, target_scores.begin(), target_scores.end(), target_block_ids.begin(), seed_hits, cfg); + return make_pair(GlobalRanking::ranking_list(query_id, l.target_scores.begin(), l.target_scores.end(), l.target_block_ids.begin(), l.seed_hits, cfg), Stats()); } - return extend(query_id, cfg, stat, flags, seed_hits, target_block_ids, target_scores); + pair, Stats> r = extend(query_id, cfg, stat, flags, l); + if (!trivial_matches.empty()) { + r.first.insert(r.first.end(), make_move_iterator(trivial_matches.begin()), make_move_iterator(trivial_matches.end())); + culling(r.first, cfg); + } + return r; } } \ No newline at end of file diff --git a/src/align/extend.h b/src/align/extend.h index e04960b7d..a5ab661a2 100644 --- a/src/align/extend.h +++ b/src/align/extend.h @@ -28,17 +28,14 @@ along with this program. If not, see . #include "../run/config.h" #include "../dp/flags.h" #include "../stats/cbs.h" +#include "../output/def.h" namespace Extension { -enum class Mode { - BANDED_FAST, BANDED_SLOW, FULL, GLOBAL -}; - extern const std::map default_ext_mode; struct Match { - Match(size_t target_block_id, const Sequence& seq, const Stats::TargetMatrix& matrix, int ungapped_score, int filter_score = 0, double filter_evalue = DBL_MAX): + Match(const BlockId target_block_id, const Sequence& seq, const ::Stats::TargetMatrix& matrix, Score ungapped_score, Score filter_score = 0, double filter_evalue = DBL_MAX): target_block_id(target_block_id), seq(seq), matrix(matrix), @@ -55,30 +52,22 @@ struct Match { static bool cmp_score(const Match& m, const Match& n) { return m.filter_score > n.filter_score || (m.filter_score == n.filter_score && m.target_block_id < n.target_block_id); } - Match(size_t target_block_id, const Sequence& seq, const Stats::TargetMatrix& matrix, std::array, MAX_CONTEXT> &hsp, int ungapped_score); + Match(BlockId target_block_id, const Sequence& seq, const ::Stats::TargetMatrix& matrix, std::array, MAX_CONTEXT> &hsp, int ungapped_score); + static Match self_match(BlockId query_id, Sequence query_seq); void inner_culling(); void max_hsp_culling(); - void apply_filters(int source_query_len, const char *query_title, const Sequence& query_seq, const Block& targets); - size_t target_block_id; + void apply_filters(int source_query_len, const char *query_title, const Sequence& query_seq, const double query_self_aln_score, const Block& targets, const OutputFormat* output_format); + BlockId target_block_id; Sequence seq; - Stats::TargetMatrix matrix; + ::Stats::TargetMatrix matrix; int filter_score; double filter_evalue; int ungapped_score; std::list hsp; }; -std::vector extend(size_t query_id, Search::Hit* begin, Search::Hit* end, const Search::Config &cfg, Statistics &stat, DP::Flags flags); -TextBuffer* generate_output(vector &targets, size_t query_block_id, Statistics &stat, const Search::Config& cfg); -TextBuffer* generate_intermediate_output(const vector &targets, size_t query_block_id, const Search::Config& cfg); - -/*inline int raw_score_cutoff(size_t query_len) { - return score_matrix.rawscore(config.min_bit_score == 0 ? score_matrix.bitscore(config.max_evalue, (unsigned)query_len) : config.min_bit_score); -}*/ +std::pair, Stats> extend(BlockId query_id, Search::Hit* begin, Search::Hit* end, const Search::Config &cfg, Statistics &stat, DP::Flags flags); +TextBuffer* generate_output(std::vector &targets, const Extension::Stats& stats, BlockId query_block_id, Statistics &stat, const Search::Config& cfg); +TextBuffer* generate_intermediate_output(const std::vector &targets, BlockId query_block_id, const Search::Config& cfg); } - -template<> -struct EnumTraits { - static const SEMap from_string; -}; diff --git a/src/align/extend_chunk.h b/src/align/extend_chunk.h new file mode 100644 index 000000000..5699314e9 --- /dev/null +++ b/src/align/extend_chunk.h @@ -0,0 +1,55 @@ +static size_t lazy_masking(vector::const_iterator target_block_ids, vector::const_iterator target_block_ids_end, Block& targets, const MaskingAlgo algo) { + if (algo == MaskingAlgo::NONE) + return 0; + vector seq; + const Masking& masking = Masking::get(); + size_t n = 0; + for (auto i = target_block_ids; i != target_block_ids_end; ++i) + if (targets.fetch_seq_if_unmasked(*i, seq)) { + masking(seq.data(), seq.size(), algo, *i); + targets.write_masked_seq(*i, seq); + ++n; + } + return n; +} + +pair, Stats> extend(BlockId query_id, + const Sequence *query_seq, + int source_query_len, + const Bias_correction *query_cb, + const ::Stats::Composition& query_comp, + FlatArray::Iterator seed_hits, + FlatArray::Iterator seed_hits_end, + vector::const_iterator target_block_ids, + const Search::Config& cfg, + Statistics& stat, + DP::Flags flags, + const HspValues hsp_values) +{ + static const Loc GAPPED_FILTER_MIN_QLEN = 85; + const int64_t n = seed_hits_end - seed_hits; + stat.inc(Statistics::TARGET_HITS2, n); + task_timer timer(flag_any(flags, DP::Flags::PARALLEL) ? config.target_parallel_verbosity : UINT_MAX); + + if (cfg.lazy_masking && !config.global_ranking_targets) + stat.inc(Statistics::MASKED_LAZY, lazy_masking(target_block_ids, target_block_ids + n, *cfg.target, cfg.target_masking)); + + pair, vector> gf; + if (cfg.gapped_filter_evalue > 0.0 && config.global_ranking_targets == 0 && (!align_mode.query_translated || query_seq[0].length() >= GAPPED_FILTER_MIN_QLEN)) { + timer.go("Computing gapped filter"); + gf = gapped_filter(query_seq, query_cb, seed_hits, seed_hits_end, target_block_ids, stat, flags, cfg); + if (!flag_any(flags, DP::Flags::PARALLEL)) + stat.inc(Statistics::TIME_GAPPED_FILTER, timer.microseconds()); + seed_hits = gf.first.begin(); + seed_hits_end = gf.first.end(); + target_block_ids = gf.second.cbegin(); + } + stat.inc(Statistics::TARGET_HITS3, seed_hits_end - seed_hits); + + timer.go("Computing chaining"); + vector targets = ungapped_stage(query_seq, query_cb, query_comp, seed_hits, seed_hits_end, target_block_ids, flags, stat, *cfg.target, cfg.extension_mode); + if (!flag_any(flags, DP::Flags::PARALLEL)) + stat.inc(Statistics::TIME_CHAINING, timer.microseconds()); + + return align(targets, query_seq, cfg.query->ids()[query_id], query_cb, source_query_len, flags, hsp_values, cfg.extension_mode, *cfg.thread_pool, cfg, stat); +} diff --git a/src/align/full_db.cpp b/src/align/full_db.cpp index dd01dd13d..82e29cea7 100644 --- a/src/align/full_db.cpp +++ b/src/align/full_db.cpp @@ -4,37 +4,40 @@ using std::list; using std::map; +using std::vector; namespace Extension { vector full_db_align(const Sequence* query_seq, const Bias_correction* query_cb, DP::Flags flags, const HspValues hsp_values, Statistics& stat, const Block& target_block) { vector v; vector r; - Stats::TargetMatrix matrix; + ::Stats::TargetMatrix matrix; list hsp; const SequenceSet& ref_seqs = target_block.seqs(); for (int frame = 0; frame < align_mode.query_contexts; ++frame) { DP::Params params{ query_seq[frame], + "", Frame(frame), query_seq[frame].length(), - Stats::CBS::hauser(config.comp_based_stats) ? query_cb[frame].int8.data() : nullptr, + ::Stats::CBS::hauser(config.comp_based_stats) ? query_cb[frame].int8.data() : nullptr, flags | DP::Flags::FULL_MATRIX, hsp_values, - stat + stat, + nullptr }; list frame_hsp = DP::BandedSwipe::swipe_set(ref_seqs.cbegin(), ref_seqs.cend(), params); hsp.splice(hsp.begin(), frame_hsp, frame_hsp.begin(), frame_hsp.end()); } - map subject_idx; + map subject_idx; while (!hsp.empty()) { - size_t block_id = hsp.begin()->swipe_target; - const auto it = subject_idx.emplace(block_id, (unsigned)r.size()); + BlockId block_id = hsp.begin()->swipe_target; + const auto it = subject_idx.emplace(block_id, (BlockId)r.size()); if (it.second) r.emplace_back(block_id, ref_seqs[block_id], 0, matrix); - unsigned i = it.first->second; + BlockId i = it.first->second; r[i].add_hit(hsp, hsp.begin()); } diff --git a/src/align/gapped_filter.cpp b/src/align/gapped_filter.cpp index 940e1addc..4ccccb3f3 100644 --- a/src/align/gapped_filter.cpp +++ b/src/align/gapped_filter.cpp @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2020-2021 Max Planck Society for the Advancement of Science e.V. Code developed by Benjamin Buchfink @@ -29,10 +29,12 @@ along with this program. If not, see . #include "../dp/scan_diags.h" using std::mutex; +using std::vector; +using std::pair; namespace Extension { -int gapped_filter(const SeedHit &hit, const LongScoreProfile *query_profile, const Sequence &target, int band, int window, std::function f) { +int gapped_filter(const SeedHit &hit, const LongScoreProfile *query_profile, const Sequence &target, int band, int window, std::function f) { const int slen = (int)target.length(); const int d = std::max(hit.diag() - band / 2, -(slen - 1)), j0 = std::max(hit.j - window, 0), @@ -42,13 +44,13 @@ int gapped_filter(const SeedHit &hit, const LongScoreProfile *query_profile, con return DP::diag_alignment(scores, band); } -bool gapped_filter(FlatArray::ConstIterator begin, FlatArray::ConstIterator end, const LongScoreProfile *query_profile, uint32_t target_block_id, Statistics &stat, const Search::Config ¶ms) { +bool gapped_filter(FlatArray::DataConstIterator begin, FlatArray::DataConstIterator end, const LongScoreProfile*query_profile, uint32_t target_block_id, Statistics &stat, const Search::Config ¶ms) { constexpr int window1 = 100, MIN_STAGE2_QLEN = 100; const int qlen = (int)query_profile->length(); const Sequence target = params.target->seqs()[target_block_id]; const int slen = (int)target.length(); - for (FlatArray::ConstIterator hit = begin; hit < end; ++hit) { + for (FlatArray::DataConstIterator hit = begin; hit < end; ++hit) { stat.inc(Statistics::GAPPED_FILTER_HITS1); const int f1 = gapped_filter(*hit, query_profile, target, 64, window1, DP::scan_diags64); if (f1 > params.cutoff_gapped1_new(qlen, slen)) { @@ -63,36 +65,34 @@ bool gapped_filter(FlatArray::ConstIterator begin, FlatArray:: return false; } -void gapped_filter_worker(size_t i, size_t thread_id, const LongScoreProfile *query_profile, const FlatArray* seed_hits, const uint32_t* target_block_ids, FlatArray* out, vector *target_ids_out, mutex* mtx, const Search::Config *params) { +void gapped_filter_worker(size_t i, size_t thread_id, const LongScoreProfile*query_profile, FlatArray::Iterator seed_hits, vector::const_iterator target_block_ids, FlatArray* out, vector *target_ids_out, mutex* mtx, const Search::Config *params) { thread_local Statistics stat; - if (gapped_filter(seed_hits->begin(i), seed_hits->end(i), query_profile, target_block_ids[i], stat, *params)) { + if (gapped_filter(seed_hits.begin(i), seed_hits.end(i), query_profile, target_block_ids[i], stat, *params)) { std::lock_guard guard(*mtx); target_ids_out->push_back(target_block_ids[i]); - out->push_back(seed_hits->begin(i), seed_hits->end(i)); + out->push_back(seed_hits.begin(i), seed_hits.end(i)); } } -void gapped_filter(const Sequence* query, const Bias_correction* query_cbs, FlatArray& seed_hits, std::vector& target_block_ids, Statistics& stat, DP::Flags flags, const Search::Config ¶ms) { - if (seed_hits.size() == 0) - return; - vector query_profile; - query_profile.reserve(align_mode.query_contexts); - for (int i = 0; i < align_mode.query_contexts; ++i) - if(Stats::CBS::hauser(config.comp_based_stats)) - query_profile.emplace_back(query[i], query_cbs[i]); - else - query_profile.emplace_back(query[i]); - +pair, vector> gapped_filter(const Sequence* query, const Bias_correction* query_cbs, FlatArray::Iterator seed_hits, FlatArray::Iterator seed_hits_end, vector::const_iterator target_block_ids, Statistics& stat, DP::Flags flags, const Search::Config ¶ms) { + const int64_t n = seed_hits_end - seed_hits; FlatArray hits_out; vector target_ids_out; + if (n == 0) + return make_pair(hits_out, target_ids_out); + + vector> query_profile; + query_profile.reserve(align_mode.query_contexts); + for (int i = 0; i < align_mode.query_contexts; ++i) + query_profile.push_back(DP::make_profile8(query[i], ::Stats::CBS::hauser(config.comp_based_stats) ? query_cbs[i].int8.data() : nullptr, 0)); if(flag_any(flags, DP::Flags::PARALLEL)) { mutex mtx; - Util::Parallel::scheduled_thread_pool_auto(config.threads_, seed_hits.size(), gapped_filter_worker, query_profile.data(), &seed_hits, target_block_ids.data(), &hits_out, &target_ids_out, &mtx, ¶ms); + Util::Parallel::scheduled_thread_pool_auto(config.threads_, n, gapped_filter_worker, query_profile.data(), seed_hits, target_block_ids, &hits_out, &target_ids_out, &mtx, ¶ms); } else { - for (size_t i = 0; i < seed_hits.size(); ++i) { + for (int64_t i = 0; i < n; ++i) { if (gapped_filter(seed_hits.begin(i), seed_hits.end(i), query_profile.data(), target_block_ids[i], stat, params)) { target_ids_out.push_back(target_block_ids[i]); hits_out.push_back(seed_hits.begin(i), seed_hits.end(i)); @@ -101,8 +101,7 @@ void gapped_filter(const Sequence* query, const Bias_correction* query_cbs, Flat } - seed_hits = std::move(hits_out); - target_block_ids = std::move(target_ids_out); + return make_pair(hits_out, target_ids_out); } } \ No newline at end of file diff --git a/src/align/gapped_final.cpp b/src/align/gapped_final.cpp index 26473d6af..9f8f0e885 100644 --- a/src/align/gapped_final.cpp +++ b/src/align/gapped_final.cpp @@ -2,14 +2,48 @@ #include "target.h" #include "../dp/dp.h" #include "../output/output_format.h" +#include "../util/util.h" +#include "def.h" using std::array; using std::list; +using std::vector; namespace Extension { +HspValues filter_hspvalues() { + HspValues hsp_values = HspValues::NONE; + if (config.max_hsps != 1) + hsp_values |= HspValues::QUERY_COORDS | HspValues::TARGET_COORDS; + if (config.min_id > 0) + hsp_values |= HspValues::IDENT | HspValues::LENGTH; + if (config.approx_min_id.get(0.0) > 0) + hsp_values |= HspValues::COORDS; + if (config.query_cover > 0) + hsp_values |= HspValues::QUERY_COORDS; + if (config.subject_cover > 0) + hsp_values |= HspValues::TARGET_COORDS; + if (config.query_or_target_cover > 0) + hsp_values |= HspValues::COORDS; + return hsp_values; +} + +static bool first_round_filter_all(const Search::Config& cfg, HspValues first_round_hsp_values) { + if (config.min_id > 0 && !flag_all(first_round_hsp_values, HspValues::IDENT | HspValues::LENGTH)) + return false; + if (config.approx_min_id.get(0.0) > 0 && !flag_all(first_round_hsp_values, HspValues::COORDS)) + return false; + if (config.query_cover > 0 && !flag_all(first_round_hsp_values, HspValues::QUERY_COORDS)) + return false; + if (config.subject_cover > 0 && !flag_all(first_round_hsp_values, HspValues::TARGET_COORDS)) + return false; + if (config.query_or_target_cover > 0 && !flag_all(first_round_hsp_values, HspValues::COORDS)) + return false; + return true; +} + static void add_dp_targets(const Target& target, int target_idx, const Sequence* query_seq, array& dp_targets, DP::Flags flags, const HspValues hsp_values, const Mode mode) { - const Stats::TargetMatrix* matrix = target.adjusted_matrix() ? &target.matrix : nullptr; + const ::Stats::TargetMatrix* matrix = target.adjusted_matrix() ? &target.matrix : nullptr; const Loc tlen = target.seq.length(); for (int frame = 0; frame < align_mode.query_contexts; ++frame) { const Loc qlen = query_seq[frame].length(); @@ -18,61 +52,87 @@ static void add_dp_targets(const Target& target, int target_idx, const Sequence* ? (int64_t)qlen * (int64_t)tlen : (int64_t)DpTarget::banded_cols(qlen, tlen, hsp.d_begin, hsp.d_end) * int64_t(hsp.d_end - hsp.d_begin); const int b = DP::BandedSwipe::bin(hsp_values, flag_any(flags, DP::Flags::FULL_MATRIX) ? qlen : hsp.d_end - hsp.d_begin, hsp.score, 0, dp_size, matrix ? matrix->score_width() : 0, 0); - dp_targets[frame][b].emplace_back(target.seq, tlen, hsp.d_begin, hsp.d_end, target_idx, qlen, matrix); + dp_targets[frame][b].emplace_back(target.seq, tlen, hsp.d_begin, hsp.d_end, Interval(), 0, target_idx, qlen, matrix); } } } -vector align(vector& targets, const Sequence* query_seq, const Bias_correction* query_cb, int source_query_len, DP::Flags flags, const HspValues first_round, const Mode mode, Statistics& stat) { - array dp_targets; +vector align(vector& targets, const int64_t previous_matches, const Sequence* query_seq, const char* query_id, const Bias_correction* query_cb, int source_query_len, double query_self_aln_score, DP::Flags flags, const HspValues first_round, const bool first_round_culling, Statistics& stat, const Search::Config& cfg) { + static const int64_t MIN_STEP = 16; vector r; if (targets.empty()) return r; - r.reserve(targets.size()); - - HspValues hsp_values = output_format->hsp_values; - if (config.max_hsps == 1 && flag_all(first_round, hsp_values)) { - for (Target& t : targets) + HspValues hsp_values = cfg.output_format->hsp_values; + const bool copy_all = config.max_hsps == 1 && flag_all(first_round, hsp_values) && first_round_filter_all(cfg, first_round); + if (copy_all) + r.reserve(targets.size()); + for (Target& t : targets) + if (copy_all || t.done) r.emplace_back(t.block_id, t.seq, t.matrix, t.hsp, t.ungapped_score); + if (r.size() == targets.size()) { + apply_filters(r.begin(), r.end(), source_query_len, query_id, query_self_aln_score, query_seq[0], cfg); return r; } - - if (mode == Mode::FULL) + + if (cfg.extension_mode == Mode::FULL) flags |= DP::Flags::FULL_MATRIX; - if (mode == Mode::GLOBAL) + if (cfg.extension_mode == Mode::GLOBAL) flags |= DP::Flags::SEMI_GLOBAL; - if (config.max_hsps != 1) - hsp_values |= HspValues::QUERY_COORDS | HspValues::TARGET_COORDS; + hsp_values |= filter_hspvalues(); - for (size_t i = 0; i < targets.size(); ++i) { - /*if (config.log_subject) - std::cout << "Target=" << ref_ids::get()[targets[i].block_id] << " id=" << i << endl;*/ - add_dp_targets(targets[i], i, query_seq, dp_targets, flags, hsp_values, mode); - r.emplace_back(targets[i].block_id, targets[i].seq, targets[i].matrix, targets[i].ungapped_score); - } + vector::iterator it = targets.begin(); + auto goon = [&r, &cfg, previous_matches]() { return config.toppercent == 100.0 ? ((int64_t)r.size() + previous_matches) < cfg.max_target_seqs : true; }; - for (int frame = 0; frame < align_mode.query_contexts; ++frame) { - if (dp_targets[frame].empty()) - continue; - DP::Params params{ - query_seq[frame], - Frame(frame), - source_query_len, - Stats::CBS::hauser(config.comp_based_stats) ? query_cb[frame].int8.data() : nullptr, - flags, - hsp_values, - stat - }; - list hsp = DP::BandedSwipe::swipe(dp_targets[frame], params); - while (!hsp.empty()) - r[hsp.front().swipe_target].add_hit(hsp, hsp.begin()); - } + do { + array dp_targets; + const int64_t step_size = !first_round_culling && config.toppercent == 100.0 + ? std::min(make_multiple(std::max(cfg.max_target_seqs - (int64_t)r.size(), MIN_STEP), MIN_STEP), targets.end() - it) + : targets.size(); + + r.reserve(r.size() + step_size); + + const int64_t matches_begin = r.size(); + + for (auto i = it; i < it + step_size; ++i) { + /*if (config.log_subject) + std::cout << "Target=" << ref_ids::get()[targets[i].block_id] << " id=" << i << endl;*/ + if (i->done) + continue; + add_dp_targets(*i, (int32_t)r.size(), query_seq, dp_targets, flags, hsp_values, cfg.extension_mode); + r.emplace_back(i->block_id, i->seq, i->matrix, i->ungapped_score); + } + + for (int frame = 0; frame < align_mode.query_contexts; ++frame) { + if (dp_targets[frame].empty()) + continue; + DP::Params params{ + query_seq[frame], + query_id, + Frame(frame), + source_query_len, + ::Stats::CBS::hauser(config.comp_based_stats) ? query_cb[frame].int8.data() : nullptr, + flags, + hsp_values, + stat, + cfg.thread_pool.get() + }; + list hsp = DP::BandedSwipe::swipe(dp_targets[frame], params); + while (!hsp.empty()) + r[hsp.front().swipe_target].add_hit(hsp, hsp.begin()); + } + + for (int64_t i = matches_begin; i < (int64_t)r.size(); ++i) + r[i].inner_culling(); + + apply_filters(r.begin() + matches_begin, r.end(), source_query_len, query_id, query_self_aln_score, query_seq[0], cfg); + culling(r, cfg); - for (Match& match : r) - match.inner_culling(); + stat.inc(Statistics::TARGET_HITS6, step_size); + it += step_size; - recompute_alt_hsps(r, query_seq, source_query_len, query_cb, hsp_values, stat); + } while (it < targets.end() && goon()); + recompute_alt_hsps(r.begin(), r.end(), query_seq, source_query_len, query_cb, hsp_values, stat); return r; } diff --git a/src/align/gapped_score.cpp b/src/align/gapped_score.cpp index ab027e0bf..6cae7bcae 100644 --- a/src/align/gapped_score.cpp +++ b/src/align/gapped_score.cpp @@ -19,22 +19,32 @@ along with this program. If not, see . ****/ #include +#include #include #include "target.h" #include "../dp/dp.h" -#include "../util/interval.h" +#include "../util/geo/interval.h" #include "../data/reference.h" #include "../output/output_format.h" +#include "def.h" +#include "../dp/pfscan/pfscan.h" +#include "../util/geo/geo.h" +#include "../stats/cbs.h" +#include "../dp/ungapped.h" using std::vector; using std::array; using std::list; using std::map; using std::endl; +using std::unique_ptr; +using std::make_unique; +using std::pair; +using std::numeric_limits; namespace Extension { -static int band(int len, const Mode mode) { +int band(int len, const Mode mode) { if (config.padding > 0) return config.padding; if (mode == Mode::BANDED_FAST) { @@ -67,7 +77,20 @@ static int band(int len, const Mode mode) { } } -Match::Match(size_t target_block_id, const Sequence& seq, const Stats::TargetMatrix& matrix, std::array, MAX_CONTEXT> &hsps, int ungapped_score): +static int hsp_band(int base_band, int qlen, int tlen, const ApproxHsp& hsp) { + if (config.prefix_scan && !config.classic_band) { + return std::max(Loc((hsp.d_max - hsp.d_min) * 0.15), 32); + } + if (config.narrow_band_cov == 0.0) + return base_band; + if ((double)hsp.query_range.length() / qlen >= config.narrow_band_cov + || (double)hsp.subject_range.length() / tlen >= config.narrow_band_cov) { + return (Loc)((hsp.d_max - hsp.d_min) * config.narrow_band_factor); + } + return base_band; +} + +Match::Match(BlockId target_block_id, const Sequence& seq, const ::Stats::TargetMatrix& matrix, std::array, MAX_CONTEXT> &hsps, int ungapped_score): target_block_id(target_block_id), seq(seq), matrix(matrix), @@ -87,10 +110,17 @@ Match::Match(size_t target_block_id, const Sequence& seq, const Stats::TargetMat filter_score = hsp.front().score; } -static void add_dp_targets(const WorkTarget& target, int target_idx, const Sequence* query_seq, array& dp_targets, DP::Flags flags, const HspValues hsp_values, const Mode mode) { +static void add_dp_targets(const WorkTarget& target, + int target_idx, + const Sequence* query_seq, + array& dp_targets, + DP::Flags flags, const HspValues hsp_values, + const Mode mode, + const Search::Config& cfg) +{ const Loc band = Extension::band(query_seq->length(), mode), slen = target.seq.length(); - const Stats::TargetMatrix* matrix = target.matrix.scores.empty() ? nullptr : &target.matrix; + const ::Stats::TargetMatrix* matrix = target.matrix.scores.empty() ? nullptr : &target.matrix; const unsigned score_width = matrix ? matrix->score_width() : 0; for (int frame = 0; frame < align_mode.query_contexts; ++frame) { @@ -100,53 +130,70 @@ static void add_dp_targets(const WorkTarget& target, int target_idx, const Seque if (target.ungapped_score[frame] == 0) continue; const unsigned b = DP::BandedSwipe::bin(hsp_values, qlen, 0, target.ungapped_score[frame], (int64_t)qlen * (int64_t)slen, score_width, 0); - dp_targets[frame][b].emplace_back(target.seq, slen, 0, 0, target_idx, qlen, matrix); + dp_targets[frame][b].emplace_back(target.seq, slen, 0, 0, Interval(), 0, target_idx, qlen, matrix); continue; } if (target.hsp[frame].empty()) continue; int d0 = INT_MAX, d1 = INT_MIN, score = 0; + Loc j_min = numeric_limits::max(), j_max = numeric_limits::min(); + Anchor anchor; - for (const Hsp_traits &hsp : target.hsp[frame]) { - const int b0 = std::max(hsp.d_min - band, -(slen - 1)), - b1 = std::min(hsp.d_max + 1 + band, qlen); - const double overlap = intersect(interval(d0, d1), interval(b0, b1)).length(); - if (overlap / (d1 - d0) > config.min_band_overlap || overlap / (b1 - b0) > config.min_band_overlap) { + for (const ApproxHsp &hsp : target.hsp[frame]) { + Geo::assert_diag_bounds(hsp.d_max, qlen, slen); + Geo::assert_diag_bounds(hsp.d_min, qlen, slen); + assert(hsp.score > 0); + assert(hsp.max_diag.score > 0); + const int b = hsp_band(band, qlen, slen, hsp); + const int b0 = std::max(hsp.d_min - b, -(slen - 1)), + b1 = std::min(hsp.d_max + 1 + b, qlen); + const double overlap = intersect(Interval(d0, d1), Interval(b0, b1)).length(); + if ((overlap / (d1 - d0) > config.min_band_overlap || overlap / (b1 - b0) > config.min_band_overlap) && !config.anchored_swipe) { d0 = std::min(d0, b0); d1 = std::max(d1, b1); score = std::max(score, hsp.score); + j_min = std::min(j_min, hsp.subject_range.begin_); + j_max = std::max(j_max, hsp.subject_range.end_); + if (hsp.max_diag.score > anchor.score) anchor = hsp.max_diag; } else { if (d0 != INT_MAX) { const int64_t dp_size = (int64_t)DpTarget::banded_cols(qlen, slen, d0, d1) * int64_t(d1 - d0); const auto bin = DP::BandedSwipe::bin(hsp_values, d1 - d0, 0, score, dp_size, score_width, 0); - dp_targets[frame][bin].emplace_back(target.seq, slen, d0, d1, target_idx, qlen, matrix); + dp_targets[frame][bin].emplace_back(target.seq, slen, d0, d1, Interval(j_min, j_max), score, target_idx, qlen, matrix, DpTarget::CarryOver(), anchor); } d0 = b0; d1 = b1; score = hsp.score; + j_min = hsp.subject_range.begin_; + j_max = hsp.subject_range.end_; + anchor = hsp.max_diag; } } const int64_t dp_size = (int64_t)DpTarget::banded_cols(qlen, slen, d0, d1) * int64_t(d1 - d0); const auto bin = DP::BandedSwipe::bin(hsp_values, d1 - d0, 0, score, dp_size, score_width, 0); - dp_targets[frame][bin].emplace_back(target.seq, slen, d0, d1, target_idx, qlen, matrix); + dp_targets[frame][bin].emplace_back(target.seq, slen, d0, d1, Interval(j_min, j_max), score, target_idx, qlen, matrix, DpTarget::CarryOver(), anchor); } } -vector align(const vector &targets, const Sequence *query_seq, const Bias_correction *query_cb, int source_query_len, DP::Flags flags, const HspValues hsp_values, const Mode mode, Statistics &stat) { +pair, Stats> align(const vector &targets, const Sequence *query_seq, const char* query_id, const Bias_correction *query_cb, int source_query_len, DP::Flags flags, const HspValues hsp_values, const Mode mode, ThreadPool& tp, const Search::Config& cfg, Statistics &stat) { array dp_targets; vector r; if (targets.empty()) - return r; + return make_pair(r, Stats()); r.reserve(targets.size()); size_t cbs_targets = 0; + for (int i = 0; i < (int)targets.size(); ++i) { - add_dp_targets(targets[i], i, query_seq, dp_targets, flags, hsp_values, mode); - if (targets[i].adjusted_matrix()) - ++cbs_targets; r.emplace_back(targets[i].block_id, targets[i].seq, targets[i].ungapped_score.front(), targets[i].matrix); + if (targets[i].done) + r.back().add_hit(targets[i].hsp[0].front(), query_seq[targets[i].hsp[0].front().frame].length()); + else + add_dp_targets(targets[i], i, query_seq, dp_targets, flags, hsp_values, mode, cfg); + if (targets[i].adjusted_matrix()) + ++cbs_targets; } stat.inc(Statistics::TARGET_HITS3_CBS, cbs_targets); @@ -155,21 +202,57 @@ vector align(const vector &targets, const Sequence *query_se if (mode == Mode::GLOBAL) flags |= DP::Flags::SEMI_GLOBAL; + Stats stats; + for (int frame = 0; frame < align_mode.query_contexts; ++frame) { - if (dp_targets[frame].empty()) + const int64_t n = std::accumulate(dp_targets[frame].begin(), dp_targets[frame].end(), (int64_t)0, [](int64_t n, const vector& v) { return n + v.size(); }); + if (n == 0) continue; - DP::Params params{ - query_seq[frame], - Frame(frame), - source_query_len, - Stats::CBS::hauser(config.comp_based_stats) ? query_cb[frame].int8.data() : nullptr, - flags, - hsp_values, - stat - }; - list hsp = DP::BandedSwipe::swipe(dp_targets[frame], params); - while (!hsp.empty()) - r[hsp.front().swipe_target].add_hit(hsp, hsp.begin()); + stats.extension_count += n; + if (config.prefix_scan) { + LongScoreProfile p; + LongScoreProfile p8; + const bool hauser_cbs = ::Stats::CBS::hauser(config.comp_based_stats); + task_timer timer; + p = DP::make_profile16(query_seq[frame], hauser_cbs ? query_cb[frame].int8.data() : nullptr, 0); + p8 = DP::make_profile8(query_seq[frame], hauser_cbs ? query_cb[frame].int8.data() : nullptr, 0); + LongScoreProfile p_rev(p.reverse()); + LongScoreProfile p8_rev(p8.reverse()); + stat.inc(Statistics::TIME_PROFILE, timer.microseconds()); + const auto v = p.pointers(0), vr = p_rev.pointers(0); + const auto v8 = p8.pointers(0), vr8 = p8_rev.pointers(0); + for (int i = 0; i < 6; ++i) + for (const DpTarget& t : dp_targets[frame][i]) { + const char* tid = cfg.target->ids()[r[t.target_idx].block_id]; + DP::PrefixScan::Config cfg{ query_seq[frame], t.seq, query_id, tid, t.d_begin, t.d_end, + t.chaining_target_range, v.data(), vr.data(), v8.data(), vr8.data(), stat, 0, 0, t.chaining_score }; + //Hsp h = DP::PrefixScan::align(cfg); + //const DiagonalSegment anchor = make_null_anchor(t.anchor); + const Anchor anchor = make_clipped_anchor(t.anchor, query_seq[frame], hauser_cbs ? query_cb[frame].int8.data() : nullptr, t.seq); + if (anchor.score == 0) + continue; + Hsp h = DP::PrefixScan::align_anchored(anchor, cfg); + if (h.evalue <= config.max_evalue) + r[t.target_idx].add_hit(std::move(h)); + } + } + else { + DP::Params params{ + query_seq[frame], + query_id, + Frame(frame), + source_query_len, + ::Stats::CBS::hauser(config.comp_based_stats) ? query_cb[frame].int8.data() : nullptr, + flags, + hsp_values, + stat, + &tp + }; + DP::AnchoredSwipe::Config cfg{ query_seq[frame], ::Stats::CBS::hauser(config.comp_based_stats) ? query_cb[frame].int8.data() : nullptr, 0, stat, &tp }; + list hsp = config.anchored_swipe ? DP::BandedSwipe::anchored_swipe(dp_targets[frame], cfg) : DP::BandedSwipe::swipe(dp_targets[frame], params); + while (!hsp.empty()) + r[hsp.front().swipe_target].add_hit(hsp, hsp.begin()); + } } vector r2; @@ -181,7 +264,7 @@ vector align(const vector &targets, const Sequence *query_se r2.push_back(std::move(*i)); } - return r2; + return make_pair(r2, stats); } } \ No newline at end of file diff --git a/src/align/global_ranking/extend.cpp b/src/align/global_ranking/extend.cpp index b4a4fc250..f3732d6a6 100644 --- a/src/align/global_ranking/extend.cpp +++ b/src/align/global_ranking/extend.cpp @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2020-2021 Max Planck Society for the Advancement of Science e.V. Code developed by Benjamin Buchfink @@ -35,46 +35,41 @@ using std::mutex; using std::thread; using std::unordered_map; using std::endl; +using std::pair; +using std::vector; namespace Extension { namespace GlobalRanking { -typedef unordered_map TargetMap; +typedef unordered_map TargetMap; void extend_query(const QueryList& query_list, const TargetMap& db2block_id, const Search::Config& cfg, Statistics& stats) { - thread_local vector target_block_ids; - thread_local vector target_scores; - thread_local FlatArray seed_hits; + SeedHitList l; const size_t n = query_list.targets.size(); - target_block_ids.clear(); - target_block_ids.reserve(n); - target_scores.clear(); - target_scores.reserve(n); - seed_hits.clear(); - seed_hits.reserve(n); + l.target_block_ids.reserve(n); + l.target_scores.reserve(n); + l.seed_hits.reserve(n, 0); for (size_t i = 0; i < n; ++i) { - target_block_ids.push_back(db2block_id.at(query_list.targets[i].database_id)); - target_scores.push_back({ (uint32_t)i, query_list.targets[i].score }); - seed_hits.next(); - seed_hits.push_back({ 0,0,query_list.targets[i].score,0 }); + l.target_block_ids.push_back(db2block_id.at(query_list.targets[i].database_id)); + l.target_scores.push_back({ (uint32_t)i, query_list.targets[i].score }); + l.seed_hits.next(); + l.seed_hits.push_back({ 0,0,query_list.targets[i].score,0 }); } DP::Flags flags = DP::Flags::FULL_MATRIX; - vector matches = Extension::extend( + pair, Stats> matches = Extension::extend( query_list.query_block_id, cfg, stats, flags, - seed_hits, - target_block_ids, - target_scores); + l); - TextBuffer* buf = Extension::generate_output(matches, query_list.query_block_id, stats, cfg); - if (!matches.empty() && (!config.unaligned.empty() || !config.aligned_file.empty())) { + TextBuffer* buf = Extension::generate_output(matches.first, matches.second, query_list.query_block_id, stats, cfg); + if (!matches.first.empty() && (!config.unaligned.empty() || !config.aligned_file.empty())) { std::lock_guard lock(query_aligned_mtx); query_aligned[query_list.query_block_id] = true; } - OutputSink::get().push(query_list.query_block_id, buf); + output_sink->push(query_list.query_block_id, buf); } void align_worker(InputFile* query_list, const TargetMap* db2block_id, const Search::Config* cfg, uint32_t* next_query) { @@ -83,7 +78,7 @@ void align_worker(InputFile* query_list, const TargetMap* db2block_id, const Sea Statistics stats; while (input = fetch_query_targets(*query_list, *next_query), !input.targets.empty()) { for (uint32_t i = input.last_query_block_id; i < input.query_block_id; ++i) - OutputSink::get().push(i, nullptr); + output_sink->push(i, nullptr); extend_query(input, *db2block_id, *cfg, stats); } statistics += stats; @@ -97,11 +92,11 @@ void extend(SequenceFile& db, TempFile& merged_query_list, BitVector& ranking_db task_timer timer("Loading reference sequences"); InputFile query_list(merged_query_list); db.set_seqinfo_ptr(0); - cfg.target.reset(db.load_seqs(SIZE_MAX, false, &ranking_db_filter, true)); + cfg.target.reset(db.load_seqs(INT64_MAX, &ranking_db_filter, SequenceFile::LoadFlags::SEQS)); TargetMap db2block_id; - const size_t db_count = cfg.target->seqs().size(); + const BlockId db_count = cfg.target->seqs().size(); db2block_id.reserve(db_count); - for (size_t i = 0; i < db_count; ++i) + for (BlockId i = 0; i < db_count; ++i) db2block_id[cfg.target->block_id2oid(i)] = i; timer.finish(); verbose_stream << "#Ranked database sequences: " << cfg.target->seqs().size() << endl; @@ -118,7 +113,8 @@ void extend(SequenceFile& db, TempFile& merged_query_list, BitVector& ranking_db } timer.go("Computing alignments"); - OutputSink::instance.reset(new OutputSink(0, &master_out)); + OutputWriter writer{ &master_out }; + output_sink.reset(new ReorderQueue(0, writer)); uint32_t next_query = 0; vector threads; for (size_t i = 0; i < (config.threads_align ? config.threads_align : config.threads_); ++i) @@ -128,46 +124,40 @@ void extend(SequenceFile& db, TempFile& merged_query_list, BitVector& ranking_db timer.go("Cleaning up"); query_list.close_and_delete(); + output_sink.reset(); cfg.target.reset(); } -void extend_query(size_t source_query_block_id, const TargetMap& db2block_id, Search::Config& cfg, Statistics& stats) { +void extend_query(BlockId source_query_block_id, const TargetMap& db2block_id, Search::Config& cfg, Statistics& stats) { const size_t N = config.global_ranking_targets; - thread_local vector target_block_ids; - thread_local vector target_scores; - thread_local FlatArray seed_hits; + SeedHitList l; vector::const_iterator table_begin = cfg.ranking_table->cbegin() + source_query_block_id * N, table_end = table_begin + N; while (table_end > table_begin && (table_end - 1)->score == 0) --table_end; const size_t n = table_end - table_begin; TextBuffer* buf = nullptr; if (n) { - target_block_ids.clear(); - target_block_ids.reserve(n); - target_scores.clear(); - target_scores.reserve(n); - seed_hits.clear(); - seed_hits.reserve(n); + l.target_block_ids.reserve(n); + l.target_scores.reserve(n); + l.seed_hits.reserve(n, 0); for (size_t i = 0; i < n; ++i) { - target_block_ids.push_back(db2block_id.at(table_begin[i].oid)); - target_scores.push_back({ (uint32_t)i, table_begin[i].score }); - seed_hits.next(); - seed_hits.push_back({ 0,0,table_begin[i].score, table_begin[i].context }); + l.target_block_ids.push_back(db2block_id.at(table_begin[i].oid)); + l.target_scores.push_back({ (uint32_t)i, table_begin[i].score }); + l.seed_hits.next(); + l.seed_hits.push_back({ 0,0,table_begin[i].score, table_begin[i].context }); } DP::Flags flags = DP::Flags::FULL_MATRIX; - vector matches = Extension::extend( + pair, Stats> matches = Extension::extend( source_query_block_id, cfg, stats, flags, - seed_hits, - target_block_ids, - target_scores); + l); - buf = cfg.iterated() ? Extension::generate_intermediate_output(matches, source_query_block_id, cfg) : Extension::generate_output(matches, source_query_block_id, stats, cfg); + buf = cfg.iterated() ? Extension::generate_intermediate_output(matches.first, source_query_block_id, cfg) : Extension::generate_output(matches.first, matches.second, source_query_block_id, stats, cfg); - if (!matches.empty() && cfg.track_aligned_queries) { + if (!matches.first.empty() && cfg.track_aligned_queries) { std::lock_guard lock(query_aligned_mtx); if (!query_aligned[source_query_block_id]) { query_aligned[source_query_block_id] = true; @@ -175,7 +165,7 @@ void extend_query(size_t source_query_block_id, const TargetMap& db2block_id, Se } } } - OutputSink::get().push(source_query_block_id, buf); + output_sink->push(source_query_block_id, buf); } @@ -192,11 +182,14 @@ void extend(Search::Config& cfg, Consumer& out) { const BitVector filter = db_filter(*cfg.ranking_table, cfg.db->sequence_count()); timer.go("Loading target sequences"); cfg.db->set_seqinfo_ptr(0); - cfg.target.reset(cfg.db->load_seqs(SIZE_MAX, cfg.db->load_titles() == SequenceFile::LoadTitles::SINGLE_PASS, &filter, true)); + auto flags = SequenceFile::LoadFlags::SEQS; + if (!flag_any(cfg.db->format_flags(), SequenceFile::FormatFlags::TITLES_LAZY)) + flags |= SequenceFile::LoadFlags::TITLES; + cfg.target.reset(cfg.db->load_seqs(INT64_MAX, &filter, flags)); TargetMap db2block_id; - const size_t db_count = cfg.target->seqs().size(); + const BlockId db_count = cfg.target->seqs().size(); db2block_id.reserve(db_count); - for (size_t i = 0; i < db_count; ++i) + for (BlockId i = 0; i < db_count; ++i) db2block_id[cfg.target->block_id2oid(i)] = i; timer.finish(); verbose_stream << "#Ranked database sequences: " << db_count << endl; @@ -215,20 +208,22 @@ void extend(Search::Config& cfg, Consumer& out) { } if (cfg.iterated()) { - current_ref_block = 0; + cfg.current_ref_block = 0; cfg.db->init_dict_block(0, db_count, true); } else - cfg.db->init_random_access(current_query_chunk, 0); + cfg.db->init_random_access(cfg.current_query_block, 0); timer.go("Computing alignments"); - OutputSink::instance.reset(new OutputSink(0, &out)); - std::atomic_size_t next_query(0); - const size_t query_count = cfg.query->seqs().size() / align_mode.query_contexts; + OutputWriter writer{ &out }; + output_sink.reset(new ReorderQueue(0, writer)); + + std::atomic next_query(0); + const BlockId query_count = cfg.query->seqs().size() / align_mode.query_contexts; auto worker = [&next_query, &db2block_id, &cfg, query_count] { try { Statistics stats; - size_t q; + BlockId q; while ((q = next_query++) < query_count) extend_query(q, db2block_id, cfg, stats); statistics += stats; } @@ -245,6 +240,7 @@ void extend(Search::Config& cfg, Consumer& out) { timer.go("Deallocating memory"); cfg.target.reset(); + output_sink.reset(); if (!cfg.iterated()) cfg.db->end_random_access(); else { diff --git a/src/align/global_ranking/global_ranking.cpp b/src/align/global_ranking/global_ranking.cpp index 8ef26e0a1..7402366a6 100644 --- a/src/align/global_ranking/global_ranking.cpp +++ b/src/align/global_ranking/global_ranking.cpp @@ -30,7 +30,7 @@ using std::vector; namespace Extension { namespace GlobalRanking { -uint16_t recompute_overflow_scores(FlatArray::ConstIterator begin, FlatArray::ConstIterator end, size_t query_id, uint32_t target_id, const Search::Config& cfg) { +uint16_t recompute_overflow_scores(FlatArray::DataConstIterator begin, FlatArray::DataConstIterator end, size_t query_id, uint32_t target_id, const Search::Config& cfg) { const auto query = cfg.query->seqs()[query_id]; const auto target = cfg.target->seqs()[target_id]; int score = 0; @@ -55,11 +55,11 @@ std::vector ranking_list(size_t query_id, std::vector 0) std::sort(begin, end); // should also sort by target block id - size_t n = 0; + int64_t n = 0; vector r; - r.reserve(std::min(size_t(end - begin), config.global_ranking_targets)); + r.reserve(std::min(end - begin, config.global_ranking_targets)); for (auto i = begin; i < end && n < config.global_ranking_targets; ++i, ++n) { - r.emplace_back(target_block_ids[i->target], Sequence(), Stats::TargetMatrix(), i->score); + r.emplace_back(target_block_ids[i->target], Sequence(), ::Stats::TargetMatrix(), i->score); } return r; } diff --git a/src/align/global_ranking/table.cpp b/src/align/global_ranking/table.cpp index d85f57e45..fedb72a99 100644 --- a/src/align/global_ranking/table.cpp +++ b/src/align/global_ranking/table.cpp @@ -34,6 +34,8 @@ along with this program. If not, see . using std::endl; using std::thread; +using std::pair; +using std::vector; using SeedHits = Search::Config::RankingBuffer; // #define BATCH_BINSEARCH @@ -44,13 +46,13 @@ static void get_query_hits(SeedHits::Iterator begin, SeedHits::Iterator end, vec hits.clear(); const SequenceSet& target_seqs = cfg.target->seqs(); #ifdef KEEP_TARGET_ID - auto get_target = [](const Search::Hit& hit) { return (uint64_t)hit.subject_; }; + auto get_target = [](const Search::Hit& hit) { return (BlockId)hit.subject_; }; auto it = merge_keys(begin, end, get_target); while (it.good()) { uint16_t score = 0; for (SeedHits::Iterator i = it.begin(); i != it.end(); ++i) score = std::max(score, i->score_); - hits.emplace_back((uint32_t)cfg.target->block_id2oid(it.key()), score); + hits.emplace_back((uint32_t)cfg.target->block_id2oid(it.key()), score, 0); ++it; } #else @@ -83,7 +85,7 @@ static void get_query_hits(SeedHits::Iterator begin, SeedHits::Iterator end, vec #endif } -static pair target_score(const FlatArray::Iterator begin, const FlatArray::Iterator end, const Sequence* query_seq, const Sequence& target_seq) { +static pair target_score(const FlatArray::DataIterator begin, const FlatArray::DataIterator end, const Sequence* query_seq, const Sequence& target_seq) { if (config.no_reextend) { int score = begin->score; unsigned context = begin->frame; @@ -96,13 +98,13 @@ static pair target_score(const FlatArray::Ite return { score,context }; } std::sort(begin, end); - Diagonal_segment d = xdrop_ungapped(query_seq[begin->frame], target_seq, begin->i, begin->j); + DiagonalSegment d = xdrop_ungapped(query_seq[begin->frame], nullptr, target_seq, begin->i, begin->j, false); int score = d.score; unsigned context = begin->frame; for (auto i = begin + 1; i != end; ++i) { if (d.diag() == i->diag() && d.subject_end() >= i->j) continue; - d = xdrop_ungapped(query_seq[i->frame], target_seq, i->i, i->j); + d = xdrop_ungapped(query_seq[i->frame], nullptr, target_seq, i->i, i->j, false); if (d.score > score) { score = d.score; context = i->frame; @@ -119,13 +121,10 @@ static void get_query_hits_reextend(size_t source_query_block_id, SeedHits::Iter const SequenceSet& target_seqs = cfg.target->seqs(); hits.clear(); - FlatArray seed_hits; - vector target_block_ids; - vector scores; - Extension::load_hits(begin, end, seed_hits, target_block_ids, scores, cfg.target->seqs()); - for (size_t i = 0; i < target_block_ids.size(); ++i) { - const auto score = target_score(seed_hits.begin(i), seed_hits.end(i), query_seq.data(), target_seqs[target_block_ids[i]]); - hits.emplace_back((uint32_t)cfg.target->block_id2oid(target_block_ids[i]), score.first, score.second); + Extension::SeedHitList l = Extension::load_hits(begin, end, cfg.target->seqs()); + for (size_t i = 0; i < l.target_block_ids.size(); ++i) { + const auto score = target_score(l.seed_hits.begin(i), l.seed_hits.end(i), query_seq.data(), target_seqs[l.target_block_ids[i]]); + hits.emplace_back((uint32_t)cfg.target->block_id2oid(l.target_block_ids[i]), score.first, score.second); } } @@ -134,12 +133,15 @@ static void merge_hits(const size_t query, vector& hits, vector& merge //std::sort(hits.begin(), hits.end()); vector::iterator table_begin = cfg.ranking_table->begin() + query * N, table_end = table_begin + N; while (table_end > table_begin && (table_end - 1)->score == 0) --table_end; + const auto count = table_end - table_begin; hits.insert(hits.end(), table_begin, table_end); std::sort(hits.begin(), hits.end(), Hit::CmpOidScore()); merged.clear(); std::unique_copy(hits.begin(), hits.end(), std::back_inserter(merged), Hit::CmpOid()); std::sort(merged.begin(), merged.end()); - std::copy(merged.begin(), merged.begin() + std::min(N, merged.size()), table_begin); + const auto n = std::min(N, merged.size()); + std::copy(merged.begin(), merged.begin() + n, table_begin); + merged_count += n - count; //merged.clear(); //merged_count += Util::Algo::merge_capped(table_begin, table_end, hits.begin(), hits.end(), N, std::back_inserter(merged)); //std::copy(merged.begin(), merged.end(), table_begin); @@ -156,24 +158,31 @@ void update_table(Search::Config& cfg) { #else ips4o::parallel::sort(hits.begin(), hits.end(), Search::Hit::CmpQueryTarget(), config.threads_); #endif + timer.go("Creating partition"); + auto p = Util::Algo::partition_table(hits.begin(), hits.end(), config.threads_ * 8, ::Search::Hit::SourceQuery{ align_mode.query_contexts }); timer.go("Processing seed hits"); - std::atomic_size_t merged_count(0); - auto worker = [&cfg, &merged_count](SeedHits::Iterator begin, SeedHits::Iterator end) { - auto it = merge_keys(begin, end, ::Search::Hit::SourceQuery{ align_mode.query_contexts }); - size_t n = 0; - while (it.good()) { - const size_t query = it.begin()->query_ / align_mode.query_contexts; - vector hits, merged; - get_query_hits_reextend(query, it.begin(), it.end(), hits, cfg); - merge_hits(query, hits, merged, cfg, n); - ++it; + std::atomic_size_t merged_count(0), next(0); + auto worker = [&cfg, &merged_count, &next, &p]() { + vector hits, merged; + while (true) { + auto i = next++; + if (i + 1 >= p.size()) + break; + auto begin = p[i], end = p[i + 1]; + auto it = merge_keys(begin, end, ::Search::Hit::SourceQuery{ align_mode.query_contexts }); + size_t n = 0; + while (it.good()) { + const size_t query = it.begin()->query_ / align_mode.query_contexts; + get_query_hits_reextend(query, it.begin(), it.end(), hits, cfg); + merge_hits(query, hits, merged, cfg, n); + ++it; + } + merged_count += n; } - merged_count += n; }; vector threads; - auto p = Util::Algo::partition_table(hits.begin(), hits.end(), config.threads_, ::Search::Hit::SourceQuery{ align_mode.query_contexts }); - for (size_t i = 0; i < p.size() - 1; ++i) - threads.emplace_back(worker, p[i], p[i + 1]); + for (size_t i = 0; i < config.threads_; ++i) + threads.emplace_back(worker); for (thread& t : threads) t.join(); timer.go("Deallocating seed hit list"); diff --git a/src/align/kmer_filter.cpp b/src/align/kmer_filter.cpp new file mode 100644 index 000000000..33b2bcee2 --- /dev/null +++ b/src/align/kmer_filter.cpp @@ -0,0 +1,42 @@ +#include "../util/kmer/filter.h" +#include "target.h" +#include "../basic/config.h" +#include "../dp/ungapped.h" + +using std::move; +using std::vector; +using std::pair; + +namespace Extension { + +static const Loc MAX_LEN_DIFF_TRIVIAL_ALN = 3; + +pair> kmer_filter(Sequence query, const int8_t* query_cbs, const Block& targets, const SeedHitList& l) { + const KmerFilter filter(query, config.filter_kmer_len); + SeedHitList r; + vector matches; + for (vector::const_iterator i = l.target_block_ids.begin(); i != l.target_block_ids.end(); ++i) { + const Sequence target = targets.seqs()[*i]; + + if (abs(query.length() - target.length()) <= MAX_LEN_DIFF_TRIVIAL_ALN) { + Hsp hsp = trivial(query, target, query_cbs); + if (hsp.score) { + matches.emplace_back(*i, target, ::Stats::TargetMatrix(), 0, hsp.score, hsp.evalue); + matches.back().hsp.push_back(move(hsp)); + matches.back().apply_filters(query.length(), "", query, 0, targets, nullptr); + continue; + } + } + + const pair cov = filter.covered(targets.seqs()[*i]); + if (cov.first >= config.filter_kmer_cutoff || cov.second >= config.filter_kmer_cutoff) { + const auto target = i - l.target_block_ids.begin(); + r.target_block_ids.push_back(*i); + r.seed_hits.push_back(l.seed_hits.cbegin(target), l.seed_hits.cend(target)); + r.target_scores.push_back(l.target_scores[target]); + } + } + return { move(r), move(matches) }; +} + +} \ No newline at end of file diff --git a/src/align/legacy/banded_swipe_pipeline.cpp b/src/align/legacy/banded_swipe_pipeline.cpp index 68bb33263..f997bf963 100644 --- a/src/align/legacy/banded_swipe_pipeline.cpp +++ b/src/align/legacy/banded_swipe_pipeline.cpp @@ -21,7 +21,7 @@ along with this program. If not, see . #include #include "../align.h" #include "../../dp/dp.h" -#include "../../util/interval_partition.h" +#include "../../util/geo/interval_partition.h" #include "../../util/simd.h" using std::thread; @@ -29,6 +29,7 @@ using std::list; using std::max; using std::atomic; using std::endl; +using std::vector; namespace ExtensionPipeline { namespace BandedSwipe { @@ -45,7 +46,7 @@ struct Target : public ::Target filter_score = top_hit.ungapped.score; } - interval ungapped_query_range(int query_dna_len) const + Interval ungapped_query_range(int query_dna_len) const { const Frame f = Frame(top_hit.frame_); const int i0 = std::max((int)top_hit.query_pos_ - (int)top_hit.subject_pos_, 0), @@ -65,12 +66,12 @@ struct Target : public ::Target d1 = std::min(i->diagonal() + band, d_max); } else { - v.emplace_back(subject, subject.length(), d0, d1, target_idx, 0); // set cols here? + v.emplace_back(subject, subject.length(), d0, d1, Interval(), 0, target_idx, 0); // set cols here? d0 = std::max(i->diagonal() - band, d_min); d1 = std::min(i->diagonal() + band, d_max); } } - v.emplace_back(subject, subject.length(), d0, d1, target_idx, 0); + v.emplace_back(subject, subject.length(), d0, d1, Interval(), 0, target_idx, 0); } void add(QueryMapper &mapper, vector &vf, vector &vr, int target_idx) @@ -118,7 +119,7 @@ struct Target : public ::Target bool is_outranked(const IntervalPartition &ip, int source_query_len, double rr) const { - const interval r = ungapped_query_range(source_query_len); + const Interval r = ungapped_query_range(source_query_len); if (config.toppercent == 100.0) { const int min_score = int((double)filter_score / rr); return (double)ip.covered(r, min_score, IntervalPartition::MinScore()) / r.length() * 100.0 >= config.query_range_cover; @@ -131,11 +132,11 @@ struct Target : public ::Target }; -void Pipeline::range_ranking() +void Pipeline::range_ranking(const int64_t max_target_seqs) { const double rr = config.rank_ratio == -1 ? 0.4 : config.rank_ratio; std::stable_sort(targets.begin(), targets.end(), Target::compare_score); - IntervalPartition ip((int)std::min(config.max_alignments, (size_t)INT_MAX)); + IntervalPartition ip(max_target_seqs); for (PtrVector< ::Target>::iterator i = targets.begin(); i < targets.end();) { Target* t = ((Target*)*i); if (t->is_outranked(ip, source_query_len, rr)) { @@ -156,7 +157,7 @@ Target& Pipeline::target(size_t i) void Pipeline::run_swipe(bool score_only) { vector vf, vr; - for (size_t i = 0; i < n_targets(); ++i) + for (int64_t i = 0; i < n_targets(); ++i) target(i).add(*this, vf, vr, (int)i); list hsp; hsp = banded_3frame_swipe(translated_query, FORWARD, vf.begin(), vf.end(), this->dp_stat, score_only, target_parallel); @@ -178,7 +179,7 @@ void build_ranking_worker(PtrVector<::Target>::iterator begin, PtrVector<::Targe } } -void Pipeline::run(Statistics &stat) +void Pipeline::run(Statistics &stat, const Search::Config& cfg) { task_timer timer("Init banded swipe pipeline", target_parallel ? 3 : UINT_MAX); Config::set_option(config.padding, 32); @@ -188,20 +189,20 @@ void Pipeline::run(Statistics &stat) if (!target_parallel) { timer.go("Ungapped stage"); - for (size_t i = 0; i < n_targets(); ++i) + for (int64_t i = 0; i < n_targets(); ++i) target(i).ungapped_stage(*this); timer.go("Ranking"); if (!config.query_range_culling) - rank_targets(config.rank_ratio == -1 ? 0.4 : config.rank_ratio, config.rank_factor == -1.0 ? 1e3 : config.rank_factor); + rank_targets(config.rank_ratio == -1 ? 0.4 : config.rank_ratio, config.rank_factor == -1.0 ? 1e3 : config.rank_factor, cfg.max_target_seqs); else - range_ranking(); + range_ranking(cfg.max_target_seqs); } else { timer.finish(); log_stream << "Query: " << query_id << "; Seed hits: " << seed_hits.size() << "; Targets: " << n_targets() << endl; } - if (n_targets() > config.max_alignments || config.toppercent < 100.0) { + if (n_targets() > cfg.max_target_seqs || config.toppercent < 100.0) { stat.inc(Statistics::TARGET_HITS1, n_targets()); timer.go("Swipe (score only)"); run_swipe(true); @@ -214,7 +215,7 @@ void Pipeline::run(Statistics &stat) v.resize(interval_count); vector threads; atomic next(0); - for (unsigned i = 0; i < config.threads_; ++i) + for (int i = 0; i < config.threads_; ++i) threads.emplace_back(build_ranking_worker, targets.begin(), targets.end(), &next, &intervals[i]); for (auto &t : threads) t.join(); @@ -242,20 +243,20 @@ void Pipeline::run(Statistics &stat) } else { timer.go("Score only culling"); - for (size_t i = 0; i < n_targets(); ++i) + for (int64_t i = 0; i < n_targets(); ++i) target(i).set_filter_score(); - score_only_culling(); + score_only_culling(cfg.max_target_seqs); } } timer.go("Swipe (traceback)"); stat.inc(Statistics::TARGET_HITS2, n_targets()); - for (size_t i = 0; i < n_targets(); ++i) + for (int64_t i = 0; i < n_targets(); ++i) target(i).reset(); run_swipe(false); timer.go("Inner culling"); - for (size_t i = 0; i < n_targets(); ++i) + for (int64_t i = 0; i < n_targets(); ++i) target(i).finish(*this); } diff --git a/src/align/legacy/query_mapper.cpp b/src/align/legacy/query_mapper.cpp index da4a88e08..79e52ec93 100644 --- a/src/align/legacy/query_mapper.cpp +++ b/src/align/legacy/query_mapper.cpp @@ -28,12 +28,13 @@ along with this program. If not, see . #include "../../output/output_format.h" #include "../../output/daa/daa_write.h" #include "../../output/target_culling.h" +#include "../../util/util.h" using namespace std; -bool Target::envelopes(const Hsp_traits &t, double p) const +bool Target::envelopes(const ApproxHsp &t, double p) const { - for (list::const_iterator i = ts.begin(); i != ts.end(); ++i) + for (list::const_iterator i = ts.begin(); i != ts.end(); ++i) if (t.query_source_range.overlap_factor(i->query_source_range) >= p) return true; return false; @@ -41,7 +42,7 @@ bool Target::envelopes(const Hsp_traits &t, double p) const bool Target::is_enveloped(const Target &t, double p) const { - for (list::const_iterator i = ts.begin(); i != ts.end(); ++i) + for (list::const_iterator i = ts.begin(); i != ts.end(); ++i) if (!t.envelopes(*i, p)) return false; return true; @@ -75,15 +76,15 @@ bool Target::is_outranked(const vector &v, double treshold) { return true; } -QueryMapper::QueryMapper(size_t query_id, Search::Hit* begin, Search::Hit* end, const Search::Config &metadata, bool target_parallel) : +QueryMapper::QueryMapper(size_t query_id, Search::Hit* begin, Search::Hit* end, const Search::Config &metadata) : source_hits(std::make_pair(begin, end)), query_id((unsigned)query_id), targets_finished(0), next_target(0), source_query_len(metadata.query->source_len((unsigned)query_id)), translated_query(metadata.query->translated(query_id)), - target_parallel(target_parallel), - metadata(metadata) + metadata(metadata), + target_parallel(false) { seed_hits.reserve(source_hits.second - source_hits.first); } @@ -114,14 +115,14 @@ unsigned QueryMapper::count_targets() /*const Diagonal_segment d = config.comp_based_stats ? xdrop_ungapped(query_seq(frame), query_cb[frame], ref_seqs::get()[l.first], hits[i].seed_offset_, (int)l.second) : xdrop_ungapped(query_seq(frame), ref_seqs::get()[l.first], hits[i].seed_offset_, (int)l.second);*/ if (target_parallel) { - seed_hits.emplace_back(frame, (unsigned)l.first, (unsigned)l.second, (unsigned)hits[i].seed_offset_, Diagonal_segment()); + seed_hits.emplace_back(frame, (unsigned)l.first, (unsigned)l.second, (unsigned)hits[i].seed_offset_, DiagonalSegment()); if (l.first != subject_id) { subject_id = l.first; ++n_subject; } } else { - const Diagonal_segment d = xdrop_ungapped(query_seq(frame), metadata.target->seqs()[l.first], hits[i].seed_offset_, (int)l.second); + const DiagonalSegment d = xdrop_ungapped(query_seq(frame), nullptr, metadata.target->seqs()[l.first], hits[i].seed_offset_, (int)l.second, false); if (d.score > 0) { if (l.first != subject_id) { subject_id = l.first; @@ -146,7 +147,7 @@ void QueryMapper::load_targets() targets.get(n) = new Target(i, seed_hits[i].subject_, metadata.target->seqs()[seed_hits[i].subject_], - config.taxon_k ? metadata.taxon_nodes->rank_taxid(metadata.db->taxids(oid), Rank::species) : set()); + config.taxon_k ? metadata.db->taxon_nodes().rank_taxid(metadata.db->taxids(oid), Rank::species) : set()); ++n; subject_id = seed_hits[i].subject_; } @@ -154,7 +155,7 @@ void QueryMapper::load_targets() targets[n - 1].end = seed_hits.size(); } -void QueryMapper::rank_targets(double ratio, double factor) +void QueryMapper::rank_targets(double ratio, double factor, const int64_t max_target_seqs) { if (config.taxon_k && config.toppercent == 100.0) return; @@ -165,23 +166,23 @@ void QueryMapper::rank_targets(double ratio, double factor) score = int((double)targets[0].filter_score * (1.0 - config.toppercent / 100.0) * ratio); } else { - size_t min_idx = std::min(targets.size(), config.max_alignments); + int64_t min_idx = std::min((int64_t)targets.size(), max_target_seqs); score = int((double)targets[min_idx - 1].filter_score * ratio); } - const size_t cap = (config.toppercent < 100 || config.max_alignments == std::numeric_limits::max()) ? std::numeric_limits::max() : size_t(config.max_alignments*factor); - size_t i = 0; - for (; i < targets.size(); ++i) + const int64_t cap = (config.toppercent < 100 || max_target_seqs == INT64_MAX) ? INT64_MAX : int64_t(max_target_seqs * factor); + int64_t i = 0; + for (; i < (int64_t)targets.size(); ++i) if (targets[i].filter_score < score || i >= cap) break; targets.erase(targets.begin() + i, targets.end()); } -void QueryMapper::score_only_culling() +void QueryMapper::score_only_culling(const int64_t max_target_seqs) { std::stable_sort(targets.begin(), targets.end(), config.toppercent == 100.0 ? Target::compare_evalue : Target::compare_score); - unique_ptr target_culling(TargetCulling::get()); + unique_ptr target_culling(TargetCulling::get(max_target_seqs)); const unsigned query_len = (unsigned)query_seq(0).length(); PtrVector::iterator i; for (i = targets.begin(); i target_culling(TargetCulling::get()); + unique_ptr target_culling(TargetCulling::get(cfg.max_target_seqs)); const unsigned query_len = (unsigned)query_seq(0).length(); size_t seek_pos = 0; const char *query_title = metadata.query->ids()[query_id]; - unique_ptr f(output_format->clone()); + unique_ptr f(cfg.output_format->clone()); + Output::Info info{ cfg.query->seq_info(query_id), true, cfg.db.get(), buffer, {} }; for (size_t i = 0; i < targets.size(); ++i) { - const size_t subject_id = targets[i].subject_block_id; - const unsigned database_id = metadata.target->block_id2oid(subject_id); + const BlockId subject_id = targets[i].subject_block_id; + const OId database_id = metadata.target->block_id2oid(subject_id); string target_title; size_t dict_id; - if (!blocked_processing) + if (!cfg.blocked_processing) target_title = metadata.target->has_ids() ? metadata.target->ids()[subject_id] : metadata.db->seqid(database_id); else - dict_id = metadata.target->dict_id(current_ref_block, subject_id, *metadata.db); + dict_id = metadata.target->dict_id(cfg.current_ref_block, subject_id, *metadata.db); const unsigned subject_len = (unsigned)metadata.target->seqs()[subject_id].length(); targets[i].apply_filters(source_query_len, subject_len, query_title); if (targets[i].hsps.size() == 0) @@ -237,26 +239,28 @@ bool QueryMapper::generate_output(TextBuffer &buffer, Statistics &stat) hit_hsps = 0; for (list::iterator j = targets[i].hsps.begin(); j != targets[i].hsps.end(); ++j) { + info.unaligned = false; if (config.max_hsps > 0 && hit_hsps >= config.max_hsps) break; - if (blocked_processing) { + if (cfg.blocked_processing) { if (n_hsp == 0) seek_pos = IntermediateRecord::write_query_intro(buffer, query_id); - IntermediateRecord::write(buffer, *j, query_id, dict_id, database_id); + IntermediateRecord::write(buffer, *j, query_id, dict_id, database_id, cfg.output_format.get()); } else { if (n_hsp == 0) { - if (*f == Output_format::daa) + if (*f == OutputFormat::daa) seek_pos = write_daa_query_record(buffer, query_title, align_mode.query_translated ? metadata.query->source_seqs()[query_id] : metadata.query->seqs()[query_id]); else - f->print_query_intro(query_id, query_title, source_query_len, buffer, false, metadata); + f->print_query_intro(info); } - if (*f == Output_format::daa) - write_daa_record(buffer, *j, metadata.target->dict_id(current_ref_block, subject_id, *metadata.db)); + if (*f == OutputFormat::daa) + write_daa_record(buffer, *j, safe_cast(metadata.target->dict_id(cfg.current_ref_block, subject_id, *metadata.db))); else f->print_match(HspContext(*j, query_id, + cfg.query->block_id2oid(query_id), translated_query, query_title, database_id, @@ -264,7 +268,7 @@ bool QueryMapper::generate_output(TextBuffer &buffer, Statistics &stat) target_title.c_str(), n_target_seq, hit_hsps, - metadata.target->seqs()[subject_id]), metadata, buffer); + metadata.target->seqs()[subject_id]), info); } ++n_hsp; @@ -274,21 +278,21 @@ bool QueryMapper::generate_output(TextBuffer &buffer, Statistics &stat) } if (n_hsp > 0) { - if (!blocked_processing) { - if (*f == Output_format::daa) + if (!cfg.blocked_processing) { + if (*f == OutputFormat::daa) finish_daa_query_record(buffer, seek_pos); else - f->print_query_epilog(buffer, query_title, false, metadata); + f->print_query_epilog(info); } else IntermediateRecord::finish_query(buffer, seek_pos); } - else if (!blocked_processing && *f != Output_format::daa && config.report_unaligned != 0) { - f->print_query_intro(query_id, query_title, source_query_len, buffer, true, metadata); - f->print_query_epilog(buffer, query_title, true, metadata); + else if (!cfg.blocked_processing && *f != OutputFormat::daa && config.report_unaligned != 0) { + f->print_query_intro(info); + f->print_query_epilog(info); } - if (!blocked_processing) { + if (!cfg.blocked_processing) { stat.inc(Statistics::MATCHES, n_hsp); stat.inc(Statistics::PAIRWISE, n_target_seq); if (n_hsp > 0) diff --git a/src/align/legacy/query_mapper.h b/src/align/legacy/query_mapper.h index 9a55ccf8e..455554ca1 100644 --- a/src/align/legacy/query_mapper.h +++ b/src/align/legacy/query_mapper.h @@ -29,7 +29,7 @@ along with this program. If not, see . #include "../../util/ptr_vector.h" #include "../../dp/dp.h" #include "../../data/reference.h" -#include "../../dp/hsp_traits.h" +#include "../../util/hsp/approx_hsp.h" #include "../../basic/match.h" #include "../../run/config.h" #include "../search/hit.h" @@ -38,7 +38,7 @@ struct Seed_hit { Seed_hit() {} - Seed_hit(unsigned frame, unsigned subject, unsigned subject_pos, unsigned query_pos, const Diagonal_segment &ungapped) : + Seed_hit(unsigned frame, unsigned subject, unsigned subject_pos, unsigned query_pos, const DiagonalSegment &ungapped) : frame_(frame), subject_(subject), subject_pos_(subject_pos), @@ -56,17 +56,17 @@ struct Seed_hit } bool is_enveloped(std::list::const_iterator begin, std::list::const_iterator end, int dna_len) const { - const DiagonalSegment d(ungapped, ::Frame(frame_)); + const DiagonalSegmentT d(ungapped, ::Frame(frame_)); for (std::list::const_iterator i = begin; i != end; ++i) if (i->envelopes(d, dna_len)) return true; return false; } - DiagonalSegment diagonal_segment() const + DiagonalSegmentT diagonal_segment() const { - return DiagonalSegment(ungapped, ::Frame(frame_)); + return DiagonalSegmentT(ungapped, ::Frame(frame_)); } - interval query_source_range(int dna_len) const + Interval query_source_range(int dna_len) const { return diagonal_segment().query_absolute_range(dna_len); } @@ -76,7 +76,7 @@ struct Seed_hit } static bool compare_pos(const Seed_hit &x, const Seed_hit &y) { - return Diagonal_segment::cmp_subject_end(x.ungapped, y.ungapped); + return DiagonalSegment::cmp_subject_end(x.ungapped, y.ungapped); } static bool compare_diag(const Seed_hit &x, const Seed_hit &y) { @@ -99,7 +99,7 @@ struct Seed_hit }; unsigned frame_, subject_, subject_pos_, query_pos_; - Diagonal_segment ungapped; + DiagonalSegment ungapped; unsigned prefix_score; }; @@ -109,7 +109,7 @@ struct Target filter_score(filter_score), filter_evalue(filter_evalue) {} - Target(size_t begin, unsigned subject_id, const Sequence& subject, const std::set &taxon_rank_ids) : + Target(size_t begin, unsigned subject_id, const Sequence& subject, const std::set &taxon_rank_ids) : subject_block_id(subject_id), subject(subject), filter_score(0), @@ -128,12 +128,12 @@ struct Target } void fill_source_ranges(size_t query_len) { - for (std::list::iterator i = ts.begin(); i != ts.end(); ++i) + for (std::list::iterator i = ts.begin(); i != ts.end(); ++i) i->query_source_range = TranslatedPosition::absolute_interval(TranslatedPosition(i->query_range.begin_, Frame(i->frame)), TranslatedPosition(i->query_range.end_, Frame(i->frame)), (int)query_len); } - void add_ranges(vector &v); - bool is_outranked(const vector &v, double treshold); - bool envelopes(const Hsp_traits &t, double p) const; + void add_ranges(std::vector &v); + bool is_outranked(const std::vector &v, double treshold); + bool envelopes(const ApproxHsp &t, double p) const; bool is_enveloped(const Target &t, double p) const; bool is_enveloped(PtrVector::const_iterator begin, PtrVector::const_iterator end, double p, int min_score) const; void inner_culling(); @@ -146,23 +146,23 @@ struct Target bool outranked; size_t begin, end; std::list hsps; - std::list ts; + std::list ts; Seed_hit top_hit; - std::set taxon_rank_ids; + std::set taxon_rank_ids; enum { INTERVAL = 64 }; }; struct QueryMapper { - QueryMapper(size_t query_id, Search::Hit* begin, Search::Hit* end, const Search::Config &metadata, bool target_parallel = false); + QueryMapper(size_t query_id, Search::Hit* begin, Search::Hit* end, const Search::Config &metadata); void init(); - bool generate_output(TextBuffer &buffer, Statistics &stat); - void rank_targets(double ratio, double factor); - void score_only_culling(); - size_t n_targets() const + bool generate_output(TextBuffer &buffer, Statistics &stat, const Search::Config& cfg); + void rank_targets(double ratio, double factor, const int64_t max_target_seqs); + void score_only_culling(const int64_t max_target_seqs); + int64_t n_targets() const { - return targets.size(); + return (int64_t)targets.size(); } bool finished() const { @@ -177,22 +177,22 @@ struct QueryMapper for (size_t i = 0; i < targets.size(); ++i) targets[i].fill_source_ranges(source_query_len); } - virtual void run(Statistics &stat) = 0; + virtual void run(Statistics &stat, const Search::Config& cfg) = 0; virtual ~QueryMapper() {} - pair source_hits; + std::pair source_hits; unsigned query_id, targets_finished, next_target; unsigned source_query_len, unaligned_from; PtrVector targets; - vector seed_hits; - vector query_cb; + std::vector seed_hits; + std::vector query_cb; TranslatedSequence translated_query; - bool target_parallel; const Search::Config &metadata; + bool target_parallel; private: - static pair get_query_data(); + static std::pair get_query_data(); unsigned count_targets(); Sequence query_source_seq() const { diff --git a/src/align/load_hits.h b/src/align/load_hits.h index 605fc2af4..70c12c246 100644 --- a/src/align/load_hits.h +++ b/src/align/load_hits.h @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2020-2021 Max Planck Society for the Advancement of Science e.V. Code developed by Benjamin Buchfink @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ +#include #include "target.h" #include "../search/hit.h" #include "../data/sequence_set.h" @@ -25,14 +26,31 @@ along with this program. If not, see . namespace Extension { template -void load_hits(It begin, It end, FlatArray &hits, vector &target_block_ids, vector &target_scores, const SequenceSet& ref_seqs) { - hits.clear(); - hits.reserve(end - begin); - target_block_ids.clear(); - target_scores.clear(); - if (begin >= end) - return; - std::sort(begin, end, Search::Hit::CmpSubject()); +static int64_t count_targets(It begin, It end) { + if (begin == end) + return 0; + auto last = (begin++)->subject_; + int64_t n = 1; + for (It i = begin; i < end; ++i) + if (i->subject_ != last) { + last = i->subject_; + ++n; + } + return n; +} + +template +static SeedHitList load_hits(It begin, It end, const SequenceSet& ref_seqs) { + std::sort(begin, end, Search::Hit::CmpSubject()); + const auto targets = count_targets(begin, end), hits = end - begin; + SeedHitList list; + list.seed_hits.reserve(targets, hits); + list.target_block_ids.reserve(targets); + list.target_scores.reserve(targets); + + if (hits <= 0) + return list; + const size_t total_subjects = ref_seqs.size(); unsigned target_len; uint32_t target = UINT32_MAX; @@ -40,11 +58,11 @@ void load_hits(It begin, It end, FlatArray &hits, vector &tar #ifdef HIT_KEEP_TARGET_ID if(true) { #else - if (std::log2(total_subjects) * (end - begin) < total_subjects / 10) { + if (std::log2(total_subjects) * hits < total_subjects / 10) { #endif for (auto i = begin; i < end; ++i) { #ifdef HIT_KEEP_TARGET_ID - std::pair l{ i->target_block_id, (size_t)i->subject_ - ref_seqs.position(i->target_block_id, 0) }; + std::pair l{ (size_t)i->target_block_id, (size_t)i->subject_ - ref_seqs.position(i->target_block_id, 0) }; #else std::pair l = ref_seqs.local_position((uint64_t)i->subject_); #endif @@ -52,51 +70,53 @@ void load_hits(It begin, It end, FlatArray &hits, vector &tar if (t != target) { if (target != UINT32_MAX) { #ifdef EVAL_TARGET - target_scores.push_back({ uint32_t(target_block_ids.size() - 1), score, score_matrix.evalue(score, query_len, target_len) }); + list.target_scores.push_back({ uint32_t(list.target_block_ids.size() - 1), score, score_matrix.evalue(score, query_len, target_len) }); #else - target_scores.push_back({ uint32_t(target_block_ids.size() - 1), score }); + list.target_scores.push_back({ uint32_t(list.target_block_ids.size() - 1), score }); #endif score = 0; } - hits.next(); + list.seed_hits.next(); target = t; target_len = (unsigned)ref_seqs[target].length(); - target_block_ids.push_back(target); + list.target_block_ids.push_back(target); } - hits.push_back({ (int)i->seed_offset_, (int)l.second, i->score_, i->query_ % align_mode.query_contexts }); + list.seed_hits.push_back({ (int)i->seed_offset_, (int)l.second, i->score_, i->query_ % align_mode.query_contexts }); score = std::max(score, i->score_); } } else { - typename vector::const_iterator limit_begin = ref_seqs.limits_begin(), it = limit_begin; + typename std::vector::const_iterator limit_begin = ref_seqs.limits_begin(), it = limit_begin; for (auto i = begin; i < end; ++i) { - const size_t subject_offset = (uint64_t)i->subject_; + const int64_t subject_offset = i->subject_; while (*it <= subject_offset) ++it; uint32_t t = (uint32_t)(it - limit_begin) - 1; if (t != target) { if (target != UINT32_MAX) { #ifdef EVAL_TARGET - target_scores.push_back({ uint32_t(target_block_ids.size() - 1), score, score_matrix.evalue(score, query_len, target_len) }); + list.target_scores.push_back({ uint32_t(list.target_block_ids.size() - 1), score, score_matrix.evalue(score, query_len, target_len) }); #else - target_scores.push_back({ uint32_t(target_block_ids.size() - 1), score }); + list.target_scores.push_back({ uint32_t(list.target_block_ids.size() - 1), score }); #endif score = 0; } - hits.next(); - target_block_ids.push_back(t); + list.seed_hits.next(); + list.target_block_ids.push_back(t); target = t; target_len = (unsigned)ref_seqs[target].length(); } - hits.push_back({ (int)i->seed_offset_, (int)(subject_offset - *(it - 1)), i->score_, i->query_ % align_mode.query_contexts }); + list.seed_hits.push_back({ (int)i->seed_offset_, (int)(subject_offset - *(it - 1)), i->score_, i->query_ % align_mode.query_contexts }); score = std::max(score, i->score_); } } if (target != UINT32_MAX) #ifdef EVAL_TARGET - target_scores.push_back({ uint32_t(target_block_ids.size() - 1), score, score_matrix.evalue(score, query_len, target_len) }); + list.target_scores.push_back({ uint32_t(list.target_block_ids.size() - 1), score, score_matrix.evalue(score, query_len, target_len) }); #else - target_scores.push_back({ uint32_t(target_block_ids.size() - 1), score }); + list.target_scores.push_back({ uint32_t(list.target_block_ids.size() - 1), score }); #endif + + return list; } } \ No newline at end of file diff --git a/src/align/memory.cpp b/src/align/memory.cpp deleted file mode 100644 index 1bf16abf2..000000000 --- a/src/align/memory.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/**** -DIAMOND protein aligner -Copyright (C) 2020 Max Planck Society for the Advancement of Science e.V. - -Code developed by Benjamin Buchfink - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -****/ - -#include -#include "../basic/config.h" -#include "target.h" -#include "../util/util.h" -#include "../util/algo/partition.h" - -using std::pair; - -namespace Extension { - -Memory* memory = nullptr; - -Memory::Memory(size_t query_count): - N(config.memory_intervals), - scores_(query_count * N, 0), - count_(query_count, 0), - ranking_low_score_(query_count, 0), - ranking_failed_count_(query_count, 0) -{ -} - -int& Memory::low_score(size_t query_id) { - return scores_[query_id*N + (N - 1)]; -} - -int& Memory::mid_score(size_t query_id) { - return scores_[query_id*N]; -} - -int& Memory::min_score(size_t query_id, size_t i) { - return scores_[query_id*N + i]; -} - -size_t Memory::count(size_t query_id) const { - return (size_t)count_[query_id]; -} - -static pair update_range(vector::const_iterator& begin, vector::const_iterator end, size_t size, size_t& count, int& low_score) { - if (begin >= end) - return { 0, 0 }; - auto it = begin; - size_t n = 0, cut = 0, total = count; - const int low = low_score; - while (it < end && (it->filter_score > low || total < size) && n < size) { - ++it; - ++n; - if (total < size) - ++total; - else - ++cut; - } - if (n == 0) - return { 0, 0 }; - begin = it; - --it; - if (n == total) - low_score = it->filter_score; - else if (count < total) - low_score = std::min(low_score, it->filter_score); - count = total; - return { cut, low }; -} - -void Memory::update(size_t query_id, std::vector::const_iterator begin, std::vector::const_iterator end) { - if (!config.query_memory) - return; - - const size_t cutoff = config.max_alignments; - Partition p(cutoff, N); - size_t total = count_[query_id], overflow_count = 0; - int overflow_score = 0; - for (size_t i = 0; i < p.parts; ++i) { - const size_t size = p.size(i); - size_t count = std::min(total, size); - int low_score = this->min_score(query_id, i); - if (overflow_count >= size) - low_score = std::max(low_score, overflow_score); - auto it = begin; - pair r = update_range(begin, end, size, count, low_score); - - overflow_count = r.first; - overflow_score = r.second; - this->min_score(query_id, i) = low_score; - const size_t n = begin - it; - total += n; - total -= count; - count_[query_id] += n; - } - - count_[query_id] = std::min(count_[query_id], (int)cutoff); -} - -void Memory::update_failed_count(size_t query_id, size_t failed_count, int ranking_low_score) { - if (ranking_low_score >= ranking_low_score_[query_id] && config.query_memory) { - ranking_low_score_[query_id] = ranking_low_score; - ranking_failed_count_[query_id] = failed_count; - } -} - - -} \ No newline at end of file diff --git a/src/align/output.cpp b/src/align/output.cpp index 576cbace0..2d3234bac 100644 --- a/src/align/output.cpp +++ b/src/align/output.cpp @@ -30,15 +30,15 @@ using std::vector; namespace Extension { -TextBuffer* generate_output(vector &targets, size_t query_block_id, Statistics &stat, const Search::Config& cfg) +TextBuffer* generate_output(vector &targets, const Extension::Stats& stats, BlockId query_block_id, Statistics &stat, const Search::Config& cfg) { const SequenceSet& query_seqs = cfg.query->seqs(), &ref_seqs = cfg.target->seqs(); TextBuffer* out = new TextBuffer; - std::unique_ptr f(output_format->clone()); + std::unique_ptr f(cfg.output_format->clone()); size_t seek_pos = 0; unsigned n_hsp = 0, hit_hsps = 0; + Output::Info info{ cfg.query->seq_info(query_block_id), !targets.empty(), cfg.db.get(), *out, stats }; TranslatedSequence query = query_seqs.translated_seq(align_mode.query_translated ? cfg.query->source_seqs()[query_block_id] : query_seqs[query_block_id], query_block_id * align_mode.query_contexts); - const unsigned query_len = (unsigned)query.index(0).length(); const char *query_title = cfg.query->ids()[query_block_id]; const double query_self_aln_score = cfg.query->has_self_aln() ? cfg.query->self_aln_score(query_block_id) : 0.0; const bool aligned = !targets.empty(); @@ -46,31 +46,32 @@ TextBuffer* generate_output(vector &targets, size_t query_block_id, Stati if (cfg.iterated()) { if (aligned) seek_pos = IntermediateRecord::write_query_intro(*out, query_block_id); } - else if (*f == Output_format::daa) { + else if (*f == OutputFormat::daa) { if (aligned) seek_pos = write_daa_query_record(*out, query_title, query.source()); } else if (aligned || config.report_unaligned) - f->print_query_intro(query_block_id, query_title, query.source().length(), *out, !aligned, cfg); + f->print_query_intro(info); - for (size_t i = 0; i < targets.size(); ++i) { + for (int i = 0; i < (int)targets.size(); ++i) { if (targets[i].hsp.empty()) throw std::runtime_error("generate_output: target with no hsps."); - const size_t subject_id = targets[i].target_block_id; - const unsigned database_id = cfg.target->block_id2oid(subject_id); + const BlockId subject_id = targets[i].target_block_id; + const int64_t database_id = cfg.target->block_id2oid(subject_id); const unsigned subject_len = (unsigned)ref_seqs[subject_id].length(); const double target_self_aln_score = cfg.target->has_self_aln() ? cfg.target->self_aln_score(subject_id) : 0.0; hit_hsps = 0; for (Hsp &hsp : targets[i].hsp) { - if (*f == Output_format::daa) - write_daa_record(*out, hsp, cfg.target->dict_id(current_ref_block, subject_id, *cfg.db)); + if (*f == OutputFormat::daa) + write_daa_record(*out, hsp, safe_cast(cfg.target->dict_id(cfg.current_ref_block, subject_id, *cfg.db))); else if(cfg.iterated()) - IntermediateRecord::write(*out, hsp, query_block_id, cfg.target->dict_id(current_ref_block, subject_id, *cfg.db), database_id); + IntermediateRecord::write(*out, hsp, query_block_id, cfg.target->dict_id(cfg.current_ref_block, subject_id, *cfg.db), database_id, cfg.output_format.get()); else f->print_match(HspContext(hsp, query_block_id, + cfg.query->block_id2oid(query_block_id), query, query_title, database_id, @@ -78,10 +79,10 @@ TextBuffer* generate_output(vector &targets, size_t query_block_id, Stati (cfg.target->has_ids() ? cfg.target->ids()[subject_id] : cfg.db->seqid(database_id)).c_str(), i, hit_hsps, - cfg.target->unmasked_seqs().empty() ? Sequence() : cfg.target->unmasked_seqs()[subject_id], + cfg.target->unmasked_seqs().empty() ? cfg.target->seqs()[subject_id] : cfg.target->unmasked_seqs()[subject_id], targets[i].ungapped_score, query_self_aln_score, - target_self_aln_score), cfg, *out); + target_self_aln_score), info); ++n_hsp; ++hit_hsps; @@ -96,17 +97,17 @@ TextBuffer* generate_output(vector &targets, size_t query_block_id, Stati stat.inc(Statistics::PAIRWISE, targets.size()); if (aligned) stat.inc(Statistics::ALIGNED); - if (*f == Output_format::daa) { + if (*f == OutputFormat::daa) { if (aligned) finish_daa_query_record(*out, seek_pos); } else if (aligned || config.report_unaligned) - f->print_query_epilog(*out, query_title, targets.empty(), cfg); + f->print_query_epilog(info); } return out; } -TextBuffer* generate_intermediate_output(const vector &targets, size_t query_block_id, const Search::Config& cfg) +TextBuffer* generate_intermediate_output(const vector &targets, BlockId query_block_id, const Search::Config& cfg) { TextBuffer* out = new TextBuffer; if (targets.empty()) @@ -117,11 +118,11 @@ TextBuffer* generate_intermediate_output(const vector &targets, size_t qu for (size_t i = 0; i < targets.size(); ++i) { - const size_t block_id = targets[i].target_block_id; - const size_t dict_id = target.dict_id(current_ref_block, block_id, *cfg.db); + const BlockId block_id = targets[i].target_block_id; + const size_t dict_id = target.dict_id(cfg.current_ref_block, block_id, *cfg.db); for (const Hsp &hsp : targets[i].hsp) - IntermediateRecord::write(*out, hsp, query_block_id, dict_id, target.block_id2oid(block_id)); + IntermediateRecord::write(*out, hsp, query_block_id, dict_id, target.block_id2oid(block_id), cfg.output_format.get()); /*if (config.global_ranking_targets > 0) IntermediateRecord::write(*out, subject_id, targets[i].ungapped_score, cfg);*/ } diff --git a/src/align/short.cpp b/src/align/short.cpp new file mode 100644 index 000000000..0b3a5d66a --- /dev/null +++ b/src/align/short.cpp @@ -0,0 +1,17 @@ +#include "../search/search.h" +#include "load_hits.h" + +namespace Extension { + +static const int64_t CAP = 1000; + +TextBuffer* pipeline_short(BlockId query, Search::Hit* begin, Search::Hit* end, Search::Config& cfg, Statistics& stats) { + TextBuffer* out = nullptr; + SeedHitList l = load_hits(begin, end, cfg.target->seqs()); + stats.inc(Statistics::TARGET_HITS0, l.target_block_ids.size()); + std::sort(l.target_scores.begin(), l.target_scores.end()); + stats.inc(Statistics::TARGET_HITS1, std::min((int64_t)l.target_scores.size(), CAP)); + return out; +} + +} \ No newline at end of file diff --git a/src/align/target.h b/src/align/target.h index 772b604ce..16653fa13 100644 --- a/src/align/target.h +++ b/src/align/target.h @@ -27,15 +27,16 @@ along with this program. If not, see . #include #include #include -#include "../basic/diagonal_segment.h" +#include "../util/geo/diagonal_segment.h" #include "../basic/const.h" -#include "../dp/hsp_traits.h" +#include "../util/hsp/approx_hsp.h" #include "../stats/hauser_correction.h" #include "extend.h" #include "../util/data_structures/flat_array.h" #include "../stats/cbs.h" #include "../dp/flags.h" -#include "../data/block.h" +#include "../data/block/block.h" +#include "../util/parallel/thread_pool.h" struct SequenceSet; @@ -59,46 +60,75 @@ struct SeedHit { const int d1 = diag(), d2 = x.diag(); return d1 < d2 || (d1 == d2 && j < x.j); } + Interval query_range() const { + return { i,i + 1 }; + } + Interval target_range() const { + return { j,j + 1 }; + } + DiagonalSegment diag_segment() const { + return DiagonalSegment(i, j, 1, score); + } int i, j, score; unsigned frame; }; struct WorkTarget { - WorkTarget(size_t block_id, const Sequence& seq, int query_len, const Stats::Composition& query_comp, const int16_t** query_matrix); + WorkTarget(BlockId block_id, const Sequence& seq, int query_len, const ::Stats::Composition& query_comp, const int16_t** query_matrix); bool adjusted_matrix() const { return !matrix.scores.empty(); } - size_t block_id; + BlockId block_id; Sequence seq; std::array ungapped_score; - std::array, MAX_CONTEXT> hsp; - Stats::TargetMatrix matrix; + std::array, MAX_CONTEXT> hsp; + ::Stats::TargetMatrix matrix; + bool done; }; -std::vector ungapped_stage(const Sequence* query_seq, const Bias_correction* query_cb, const Stats::Composition& query_comp, FlatArray& seed_hits, const std::vector& target_block_ids, DP::Flags flags, Statistics& stat, const Block& target_block, const Mode mode); +std::vector ungapped_stage(const Sequence *query_seq, const Bias_correction *query_cb, const ::Stats::Composition& query_comp, FlatArray::Iterator seed_hits, FlatArray::Iterator seed_hits_end, std::vector::const_iterator target_block_ids, DP::Flags flags, Statistics& stat, const Block& target_block, const Mode mode); struct Target { - Target(size_t block_id, const Sequence &seq, int ungapped_score, const Stats::TargetMatrix& matrix): + Target(const BlockId block_id, const Sequence &seq, int ungapped_score, const ::Stats::TargetMatrix& matrix): block_id(block_id), seq(seq), filter_score(0), filter_evalue(DBL_MAX), best_context(0), ungapped_score(ungapped_score), - matrix(matrix) + matrix(matrix), + done(false) {} + void add_hit(Hsp&& hsp) { + if(hsp.evalue < filter_evalue) { + filter_evalue = hsp.evalue; + filter_score = hsp.score; + best_context = hsp.frame; + } + this->hsp[hsp.frame].push_back(std::move(hsp)); + } + void add_hit(std::list &list, std::list::iterator it) { std::list &l = hsp[it->frame]; l.splice(l.end(), list, it); - if (l.back().score > filter_score) { + if (l.back().score > filter_score) { // should be evalue filter_evalue = l.back().evalue; filter_score = l.back().score; best_context = l.back().frame; } } + void add_hit(const ApproxHsp& h, Loc qlen) { + hsp[h.frame].emplace_back(h, qlen, seq.length()); + if (h.evalue < filter_evalue) { + filter_evalue = h.evalue; + filter_score = h.score; + } + done = true; + } + static bool comp_evalue(const Target &t, const Target& u) { return t.filter_evalue < u.filter_evalue || (t.filter_evalue == u.filter_evalue && comp_score(t, u)); } @@ -111,18 +141,18 @@ struct Target { return !matrix.scores.empty(); } - void apply_filters(int source_query_len, const char *query_title, const Sequence& query_seq, const Block& targets); void inner_culling(); void max_hsp_culling(); - size_t block_id; + BlockId block_id; Sequence seq; int filter_score; double filter_evalue; int best_context; int ungapped_score; std::array, MAX_CONTEXT> hsp; - Stats::TargetMatrix matrix; + ::Stats::TargetMatrix matrix; + bool done; }; struct TargetScore { @@ -140,43 +170,29 @@ struct TargetScore { } }; -void culling(std::vector& targets, int source_query_len, const char* query_title, const Sequence& query_seq, size_t min_keep, const Block& target_block); -bool append_hits(std::vector& targets, std::vector::const_iterator begin, std::vector::const_iterator end, size_t chunk_size, int source_query_len, const char* query_title, const Sequence& query_seq, const Block& target_block); +struct SeedHitList { + FlatArray seed_hits; + std::vector target_block_ids; + std::vector target_scores; +}; + +void culling(std::vector& targets, bool sort_only, const Search::Config& cfg); +void culling(std::vector& targets, const Search::Config& cfg); +bool append_hits(std::vector& targets, std::vector::iterator begin, std::vector::iterator end, const bool with_culling, const Search::Config& cfg); std::vector gapped_filter(const Sequence *query, const Bias_correction* query_cbs, std::vector& targets, Statistics &stat); -void gapped_filter(const Sequence* query, const Bias_correction* query_cbs, FlatArray &seed_hits, std::vector &target_block_ids, Statistics& stat, DP::Flags flags, const Search::Config ¶ms); -std::vector align(const std::vector &targets, const Sequence *query_seq, const Bias_correction *query_cb, int source_query_len, DP::Flags flags, const HspValues hsp_values, const Mode mode, Statistics &stat); -std::vector align(std::vector &targets, const Sequence *query_seq, const Bias_correction *query_cb, int source_query_len, DP::Flags flags, const HspValues first_round, const Mode mode, Statistics &stat); +std::pair, std::vector> gapped_filter(const Sequence* query, const Bias_correction* query_cbs, FlatArray::Iterator seed_hits, FlatArray::Iterator seed_hits_end, std::vector::const_iterator target_block_ids, Statistics& stat, DP::Flags flags, const Search::Config ¶ms); +std::pair, Stats> align(const std::vector &targets, const Sequence *query_seq, const char* query_id, const Bias_correction *query_cb, int source_query_len, DP::Flags flags, const HspValues hsp_values, const Mode mode, ThreadPool& tp, const Search::Config& cfg, Statistics &stat); +std::vector align(std::vector &targets, const int64_t previous_matches, const Sequence *query_seq, const char* query_id, const Bias_correction *query_cb, int source_query_len, double query_self_aln_score, DP::Flags flags, const HspValues first_round, const bool first_round_culling, Statistics &stat, const Search::Config& cfg); std::vector full_db_align(const Sequence *query_seq, const Bias_correction *query_cb, DP::Flags flags, const HspValues hsp_values, Statistics &stat, const Block& target_block); -void recompute_alt_hsps(vector& matches, const Sequence* query, const int query_source_len, const Bias_correction* query_cb, const HspValues v, Statistics& stats); +void recompute_alt_hsps(std::vector::iterator begin, std::vector::iterator end, const Sequence* query, const int query_source_len, const Bias_correction* query_cb, const HspValues v, Statistics& stats); +void apply_filters(std::vector::iterator begin, std::vector::iterator end, int source_query_len, const char* query_title, const double query_self_aln_score, const Sequence& query_seq, const Search::Config& cfg); +std::pair> kmer_filter(Sequence query, const int8_t* query_cbs, const Block& targets, const SeedHitList& l); -std::vector extend( - size_t query_id, +std::pair, Stats> extend( + BlockId query_id, const Search::Config& cfg, Statistics &stat, DP::Flags flags, - FlatArray& seed_hits, - std::vector& target_block_ids, - const std::vector& target_scores); - -struct Memory { - Memory(size_t query_count); - int& low_score(size_t query_id); - int& mid_score(size_t query_id); - int& min_score(size_t query_id, size_t i); - size_t count(size_t query_id) const; - void update(size_t query_id, std::vector::const_iterator begin, std::vector::const_iterator end); - void update_failed_count(size_t query_id, size_t failed_count, int ranking_low_score); - int ranking_low_score(size_t query_id) const { - return ranking_low_score_[query_id]; - } - size_t ranking_failed_count(size_t query_id) const { - return ranking_failed_count_[query_id]; - } -private: - const size_t N; - std::vector scores_, count_, ranking_low_score_, ranking_failed_count_; -}; - -extern Memory* memory; + SeedHitList &l); } diff --git a/src/align/ungapped.cpp b/src/align/ungapped.cpp index c17fabfb1..c6eca77b5 100644 --- a/src/align/ungapped.cpp +++ b/src/align/ungapped.cpp @@ -35,12 +35,15 @@ along with this program. If not, see . #include "../util/parallel/thread_pool.h" #include "../chaining/chaining.h" #include "../dp/dp.h" +#include "def.h" +#include "../util/geo/geo.h" using std::array; using std::vector; using std::list; using std::atomic; using std::mutex; +using std::pair; namespace Extension { @@ -48,85 +51,73 @@ std::vector target_matrices; std::mutex target_matrices_lock; atomic target_matrix_count(0); -WorkTarget::WorkTarget(size_t block_id, const Sequence& seq, int query_len, const Stats::Composition& query_comp, const int16_t** query_matrix) : +WorkTarget::WorkTarget(BlockId block_id, const Sequence& seq, int query_len, const ::Stats::Composition& query_comp, const int16_t** query_matrix) : block_id(block_id), - seq(seq) + seq(seq), + done(false) { ungapped_score.fill(0); - if (config.comp_based_stats == Stats::CBS::HAUSER_AND_AVG_MATRIX_ADJUST) { - const int l = (int)seq.length(); - const auto c = Stats::composition(seq); - auto r = Stats::s_TestToApplyREAdjustmentConditional(query_len, l, query_comp.data(), c.data(), score_matrix.background_freqs()); - if (r == Stats::eCompoScaleOldMatrix) - return; - if (*query_matrix == nullptr) { - *query_matrix = Stats::make_16bit_matrix(Stats::CompositionMatrixAdjust(query_len, query_len, query_comp.data(), query_comp.data(), Stats::CBS::AVG_MATRIX_SCALE, score_matrix.ideal_lambda(), score_matrix.joint_probs(), score_matrix.background_freqs())); - ++target_matrix_count; - } - if (target_matrices[block_id] == nullptr) { - int16_t* target_matrix = Stats::make_16bit_matrix(Stats::CompositionMatrixAdjust(l, l, c.data(), c.data(), Stats::CBS::AVG_MATRIX_SCALE, score_matrix.ideal_lambda(), score_matrix.joint_probs(), score_matrix.background_freqs())); - bool del = false; - { - std::lock_guard lock(target_matrices_lock); - if (target_matrices[block_id] == nullptr) - target_matrices[block_id] = target_matrix; - else del = true; - } - if (del) - delete[] target_matrix; - ++target_matrix_count; - } - matrix = Stats::TargetMatrix(*query_matrix, target_matrices[block_id]); - } - else - matrix = Stats::TargetMatrix(query_comp, query_len, seq); + matrix = ::Stats::TargetMatrix(query_comp, query_len, seq); } -WorkTarget ungapped_stage(FlatArray::Iterator begin, FlatArray::Iterator end, const Sequence *query_seq, const Bias_correction *query_cb, const Stats::Composition& query_comp, const int16_t** query_matrix, uint32_t block_id, Statistics& stat, const Block& targets, const Mode mode) { - array, MAX_CONTEXT> diagonal_segments; +WorkTarget ungapped_stage(FlatArray::DataIterator begin, FlatArray::DataIterator end, const Sequence *query_seq, const Bias_correction *query_cb, const ::Stats::Composition& query_comp, const int16_t** query_matrix, uint32_t block_id, Statistics& stat, const Block& targets, const Mode mode) { + array, MAX_CONTEXT> diagonal_segments; task_timer timer; const SequenceSet& ref_seqs = targets.seqs(), &ref_seqs_unmasked = targets.unmasked_seqs(); - const bool masking = config.comp_based_stats == Stats::CBS::COMP_BASED_STATS_AND_MATRIX_ADJUST ? Stats::use_seg_masking(query_seq[0], ref_seqs_unmasked[block_id]) : true; - WorkTarget target(block_id, masking ? ref_seqs[block_id] : ref_seqs_unmasked[block_id], Stats::count_true_aa(query_seq[0]), query_comp, query_matrix); + const bool masking = config.comp_based_stats == ::Stats::CBS::COMP_BASED_STATS_AND_MATRIX_ADJUST ? ::Stats::use_seg_masking(query_seq[0], ref_seqs_unmasked[block_id]) : true; + WorkTarget target(block_id, masking ? ref_seqs[block_id] : ref_seqs_unmasked[block_id], ::Stats::count_true_aa(query_seq[0]), query_comp, query_matrix); stat.inc(Statistics::TIME_MATRIX_ADJUST, timer.microseconds()); - if (!Stats::CBS::avg_matrix(config.comp_based_stats) && target.adjusted_matrix()) + if (target.adjusted_matrix()) stat.inc(Statistics::MATRIX_ADJUST_COUNT); if (mode == Mode::FULL) { - for (FlatArray::Iterator hit = begin; hit < end; ++hit) + for (FlatArray::DataIterator hit = begin; hit < end; ++hit) target.ungapped_score[hit->frame] = std::max(target.ungapped_score[hit->frame], hit->score); return target; } - if (end - begin == 1 && align_mode.query_translated) { + if (end - begin == 1 && align_mode.query_translated) { // ??? target.ungapped_score[begin->frame] = begin->score; - target.hsp[begin->frame].emplace_back(begin->diag(), begin->diag(), begin->score, begin->frame, interval(), interval()); + target.hsp[begin->frame].emplace_back(begin->diag(), begin->diag(), begin->score, begin->frame, begin->query_range(), begin->target_range(), begin->diag_segment()); return target; } std::sort(begin, end); - for (FlatArray::Iterator hit = begin; hit < end; ++hit) { - target.ungapped_score[hit->frame] = std::max(target.ungapped_score[hit->frame], hit->score); - if (!diagonal_segments[hit->frame].empty() && diagonal_segments[hit->frame].back().diag() == hit->diag() && diagonal_segments[hit->frame].back().subject_end() >= hit->j) + const bool with_diag_filter = config.hamming_ext || config.diag_filter_cov > 0 || config.diag_filter_id > 0; + for (FlatArray::DataIterator hit = begin; hit < end; ++hit) { + const auto f = hit->frame; + target.ungapped_score[f] = std::max(target.ungapped_score[f], hit->score); + if (!diagonal_segments[f].empty() && diagonal_segments[f].back().diag() == hit->diag() && diagonal_segments[f].back().subject_end() >= hit->j) continue; - const Diagonal_segment d = xdrop_ungapped(query_seq[hit->frame], target.seq, hit->i, hit->j); - if (d.score > 0) { - diagonal_segments[hit->frame].push_back(d); + const int8_t* cbs = ::Stats::CBS::hauser(config.comp_based_stats) ? query_cb[f].int8.data() : nullptr; + const DiagonalSegment d = xdrop_ungapped(query_seq[f], cbs, target.seq, hit->i, hit->j, with_diag_filter); + if (d.score > 0) + diagonal_segments[f].push_back(d); + } + + if (with_diag_filter) { + const ApproxHsp h = Chaining::hamming_ext(diagonal_segments[0].begin(), diagonal_segments[0].end(), query_seq[0].length(), target.seq.length()); + if (h.score > 0) { + target.done = true; + target.hsp[0].push_back(h); + return target; } + if (h.score < 0) + return target; } + for (int frame = 0; frame < align_mode.query_contexts; ++frame) { if (diagonal_segments[frame].empty()) continue; - std::stable_sort(diagonal_segments[frame].begin(), diagonal_segments[frame].end(), Diagonal_segment::cmp_diag); - pair> hsp = greedy_align(query_seq[frame], target.seq, diagonal_segments[frame].begin(), diagonal_segments[frame].end(), config.log_extend, frame); - target.hsp[frame] = std::move(hsp.second); - target.hsp[frame].sort(Hsp_traits::cmp_diag); + std::stable_sort(diagonal_segments[frame].begin(), diagonal_segments[frame].end(), DiagonalSegment::cmp_diag); + tie(std::ignore, target.hsp[frame]) = Chaining::run(query_seq[frame], target.seq, diagonal_segments[frame].begin(), diagonal_segments[frame].end(), config.log_extend, frame); + target.hsp[frame].sort(ApproxHsp::cmp_diag); } return target; } -void ungapped_stage_worker(size_t i, size_t thread_id, const Sequence *query_seq, const Bias_correction *query_cb, const Stats::Composition* query_comp, FlatArray *seed_hits, const uint32_t* target_block_ids, vector *out, mutex *mtx, Statistics* stat, const Block* targets, const Mode mode) { +void ungapped_stage_worker(size_t i, size_t thread_id, const Sequence *query_seq, const Bias_correction *query_cb, const ::Stats::Composition* query_comp, FlatArray::Iterator seed_hits, vector::const_iterator target_block_ids, vector *out, mutex *mtx, Statistics* stat, const Block* targets, const Mode mode) { Statistics stats; const int16_t* query_matrix = nullptr; - WorkTarget target = ungapped_stage(seed_hits->begin(i), seed_hits->end(i), query_seq, query_cb, *query_comp, &query_matrix, target_block_ids[i], stats, *targets, mode); + WorkTarget target = ungapped_stage(seed_hits.begin(i), seed_hits.end(i), query_seq, query_cb, *query_comp, &query_matrix, target_block_ids[i], stats, *targets, mode); { std::lock_guard guard(*mtx); out->push_back(std::move(target)); @@ -135,19 +126,27 @@ void ungapped_stage_worker(size_t i, size_t thread_id, const Sequence *query_seq delete[] query_matrix; } -vector ungapped_stage(const Sequence *query_seq, const Bias_correction *query_cb, const Stats::Composition& query_comp, FlatArray &seed_hits, const vector& target_block_ids, DP::Flags flags, Statistics& stat, const Block& target_block, const Mode mode) { +vector ungapped_stage(const Sequence *query_seq, const Bias_correction *query_cb, const ::Stats::Composition& query_comp, FlatArray::Iterator seed_hits, FlatArray::Iterator seed_hits_end, vector::const_iterator target_block_ids, DP::Flags flags, Statistics& stat, const Block& target_block, const Mode mode) { vector targets; - if (target_block_ids.size() == 0) + const int64_t n = seed_hits_end - seed_hits; + if(n == 0) return targets; - targets.reserve(target_block_ids.size()); + targets.reserve(n); const int16_t* query_matrix = nullptr; if (flag_any(flags, DP::Flags::PARALLEL)) { mutex mtx; - Util::Parallel::scheduled_thread_pool_auto(config.threads_, seed_hits.size(), ungapped_stage_worker, query_seq, query_cb, &query_comp, &seed_hits, target_block_ids.data(), &targets, &mtx, &stat, &target_block, mode); + Util::Parallel::scheduled_thread_pool_auto(config.threads_, n, ungapped_stage_worker, query_seq, query_cb, &query_comp, seed_hits, target_block_ids, &targets, &mtx, &stat, &target_block, mode); } else { - for (size_t i = 0; i < target_block_ids.size(); ++i) + for (int64_t i = 0; i < n; ++i) { targets.push_back(ungapped_stage(seed_hits.begin(i), seed_hits.end(i), query_seq, query_cb, query_comp, &query_matrix, target_block_ids[i], stat, target_block, mode)); + for (const ApproxHsp& hsp : targets.back().hsp[0]) { + Geo::assert_diag_bounds(hsp.d_max, query_seq[0].length(), targets.back().seq.length()); + Geo::assert_diag_bounds(hsp.d_min, query_seq[0].length(), targets.back().seq.length()); + assert(hsp.score > 0); + assert(hsp.max_diag.score > 0); + } + } } delete[] query_matrix; diff --git a/src/basic/basic.cpp b/src/basic/basic.cpp index 0a0c5039d..98df80bbc 100644 --- a/src/basic/basic.cpp +++ b/src/basic/basic.cpp @@ -22,46 +22,60 @@ along with this program. If not, see . #include "value.h" #include "shape_config.h" -#include "translate.h" +#include "../util/sequence/translate.h" #include "statistics.h" #include "sequence.h" #include "../masking/masking.h" #include "../util/util.h" #include "../stats/standard_matrix.h" -const char* Const::version_string = "2.0.15"; +const char* Const::version_string = "2.1.0"; +using std::string; +using std::vector; +using std::count; + + const char* Const::program_name = "diamond"; -Align_mode::Align_mode(unsigned mode) : +AlignMode::AlignMode(unsigned mode) : mode(mode) { - sequence_type = amino_acid; + sequence_type = SequenceType::amino_acid; switch (mode) { case blastx: - input_sequence_type = nucleotide; + input_sequence_type = SequenceType::nucleotide; query_contexts = 6; query_translated = true; query_len_factor = 3; break; + case blastn: + input_sequence_type = SequenceType::nucleotide; + query_translated = false; + query_contexts = 2; + query_len_factor = 1; + sequence_type = SequenceType::nucleotide; + break; default: - input_sequence_type = amino_acid; + input_sequence_type = SequenceType::amino_acid; query_contexts = 1; query_translated = false; query_len_factor = 1; } } -unsigned Align_mode::from_command(unsigned command) +unsigned AlignMode::from_command(unsigned command) { switch (command) { case Config::blastx: return blastx; + case Config::blastn: + return blastn; default: return blastp; } } -Align_mode align_mode (Align_mode::blastp); +AlignMode align_mode(AlignMode::blastp); Statistics statistics; ShapeConfig shapes; @@ -120,9 +134,9 @@ void Translator::init(unsigned id) } for (unsigned i = 0; i < 4; ++i) for (unsigned j = 0; j < 4; ++j) { - if (equal(lookup[i][j], 4)) + if (count(lookup[i][j], lookup[i][j] + 4, lookup[i][j][0]) == 4) lookup[i][j][4] = lookup[i][j][0]; - if (equal(lookupReverse[i][j], 4)) + if (count(lookupReverse[i][j], lookupReverse[i][j] + 4, lookupReverse[i][j][0]) == 4) lookupReverse[i][j][4] = lookupReverse[i][j][0]; } } @@ -182,6 +196,7 @@ void Statistics::print() const log_stream << "Target hits (stage 3) = " << data_[TARGET_HITS3] << " (" << data_[TARGET_HITS3_CBS] << " (" << (double)data_[TARGET_HITS3_CBS] * 100.0 / data_[TARGET_HITS3] << "%) with CBS)" << endl; log_stream << "Target hits (stage 4) = " << data_[TARGET_HITS4] << endl; log_stream << "Target hits (stage 5) = " << data_[TARGET_HITS5] << endl; + log_stream << "Target hits (stage 6) = " << data_[TARGET_HITS6] << endl; log_stream << "Swipe realignments = " << data_[SWIPE_REALIGN] << endl; if (data_[MASKED_LAZY]) log_stream << "Lazy maskings = " << data_[MASKED_LAZY] << endl; @@ -189,6 +204,15 @@ void Statistics::print() const log_stream << "Extensions (8 bit) = " << data_[EXT8] << endl; log_stream << "Extensions (16 bit) = " << data_[EXT16] << endl; log_stream << "Extensions (32 bit) = " << data_[EXT32] << endl; + log_stream << "Overflows (8 bit) = " << data_[EXT_OVERFLOW_8] << endl; + log_stream << "Wasted (16 bit) = " << data_[EXT_WASTED_16] << endl; + log_stream << "Effort (Extension) = " << 2 * data_[EXT16] + data_[EXT8] << endl; + log_stream << "Effort (Cells) = " << 2 * data_[DP_CELLS_16] + data_[DP_CELLS_8] << endl; + log_stream << "Cells (8 bit) = " << data_[DP_CELLS_8] << endl; + log_stream << "Cells (16 bit) = " << data_[DP_CELLS_16] << endl; + log_stream << "SWIPE tasks = " << data_[SWIPE_TASKS_TOTAL] << endl; + log_stream << "SWIPE tasks (async) = " << data_[SWIPE_TASKS_ASYNC] << endl; + log_stream << "Trivial aln = " << data_[TRIVIAL_ALN] << endl; log_stream << "Hard queries = " << data_[HARD_QUERIES] << endl; #ifdef DP_STAT log_stream << "Gross DP Cells = " << data_[GROSS_DP_CELLS] << endl; @@ -203,8 +227,15 @@ void Statistics::print() const log_stream << "Time (Matrix adjust) = " << (double)data_[TIME_MATRIX_ADJUST] / 1e6 << "s (CPU)" << endl; log_stream << "Time (Chaining) = " << (double)data_[TIME_CHAINING] / 1e6 << "s (CPU)" << endl; log_stream << "Time (DP target sorting) = " << (double)data_[TIME_TARGET_SORT] / 1e6 << "s (CPU)" << endl; + log_stream << "Time (Query profiles) = " << (double)data_[TIME_PROFILE] / 1e6 << "s (CPU)" << endl; log_stream << "Time (Smith Waterman) = " << (double)data_[TIME_SW] / 1e6 << "s (CPU)" << endl; + log_stream << "Time (Anchored SWIPE Alloc) = " << (double)data_[TIME_ANCHORED_SWIPE_ALLOC] / 1e6 << "s (CPU)" << endl; + log_stream << "Time (Anchored SWIPE Sort) = " << (double)data_[TIME_ANCHORED_SWIPE_SORT] / 1e6 << "s (CPU)" << endl; + log_stream << "Time (Anchored SWIPE Add) = " << (double)data_[TIME_ANCHORED_SWIPE_ADD] / 1e6 << "s (CPU)" << endl; + log_stream << "Time (Anchored SWIPE Output) = " << (double)data_[TIME_ANCHORED_SWIPE_OUTPUT] / 1e6 << "s (CPU)" << endl; + log_stream << "Time (Anchored SWIPE) = " << (double)data_[TIME_ANCHORED_SWIPE] / 1e6 << "s (CPU)" << endl; log_stream << "Time (Smith Waterman TB) = " << (double)data_[TIME_TRACEBACK_SW] / 1e6 << "s (CPU)" << endl; + log_stream << "Time (Smith Waterman-32) = " << (double)data_[TIME_EXT_32] / 1e6 << "s (CPU)" << endl; log_stream << "Time (Traceback) = " << (double)data_[TIME_TRACEBACK] / 1e6 << "s (CPU)" << endl; log_stream << "Time (Target parallel) = " << (double)data_[TIME_TARGET_PARALLEL] / 1e6 << "s (wall)" << endl; log_stream << "Time (Load seed hits) = " << (double)data_[TIME_LOAD_SEED_HITS] / 1e6 << "s (wall)" << endl; @@ -238,7 +269,7 @@ Reduction::Reduction(const char* definition_string) const vector tokens(tokenize(definition_string, " ")); size_ = (unsigned)tokens.size(); bit_size_exact_ = log(size_) / log(2); - bit_size_ = (uint64_t)ceil(bit_size_exact_); + bit_size_ = (int)ceil(bit_size_exact_); freq_.fill(0.0); for (unsigned i = 0; i < size_; ++i) for (unsigned j = 0; j < tokens[i].length(); ++j) { diff --git a/src/basic/config.cpp b/src/basic/config.cpp index e02b0e929..9c7e04f34 100644 --- a/src/basic/config.cpp +++ b/src/basic/config.cpp @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2022 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -39,12 +39,13 @@ along with this program. If not, see . #include "../util/io/temp_file.h" #include "../basic/match.h" #include "../cluster/cluster_registry.h" -#include "../basic/translate.h" +#include "../util/sequence/translate.h" #include "../dp/dp.h" #include "../masking/masking.h" #include "../util/system/system.h" #include "../util/simd.h" #include "../util/parallel/multiprocessing.h" +#include "../search/search.h" using std::thread; using std::stringstream; @@ -53,8 +54,11 @@ using std::cerr; using std::ostream; using std::cout; using std::unique_ptr; +using std::string; +using std::pair; const EMap EnumTraits::to_string = { + { Sensitivity::FASTER, "faster" }, { Sensitivity::FAST, "fast" }, { Sensitivity::DEFAULT, "default" }, { Sensitivity::MID_SENSITIVE, "mid-sensitive" }, @@ -65,6 +69,7 @@ const EMap EnumTraits::to_string = { }; const SEMap EnumTraits::from_string = { + { "faster", Sensitivity::FASTER }, { "fast", Sensitivity::FAST }, { "default", Sensitivity::DEFAULT }, { "mid-sensitive", Sensitivity::MID_SENSITIVE }, @@ -74,15 +79,45 @@ const SEMap EnumTraits::from_string = { { "ultra-sensitive", Sensitivity::ULTRA_SENSITIVE } }; +const SEMap EnumTraits::from_string = { + { "gvc", GraphAlgo::GREEDY_VERTEX_COVER }, + { "len", GraphAlgo::LEN_SORTED } +}; + const EMap EnumTraits::to_string = { { Config::Algo::DOUBLE_INDEXED, "Double-indexed" }, { Config::Algo::QUERY_INDEXED, "Query-indexed"}, {Config::Algo::CTG_SEED, "Query-indexed with contiguous seed"} }; const SEMap EnumTraits::from_string = { {"", Config::Algo::AUTO}, { "0", Config::Algo::DOUBLE_INDEXED}, {"1", Config::Algo::QUERY_INDEXED}, {"ctg", Config::Algo::CTG_SEED} }; +const EMap EnumTraits::to_string = { {SequenceType::amino_acid,"prot"}, { SequenceType::nucleotide,"nucl"} }; +const SEMap EnumTraits::from_string = { {"prot",SequenceType::amino_acid}, {"nucl",SequenceType::nucleotide} }; + Config config; +pair block_size(int64_t memory_limit, Sensitivity s, bool lin) { + const double m = (double)memory_limit / 1e9; + const int min = std::max(Search::sensitivity_traits[(int)align_mode.sequence_type].at(s).minimizer_window, 1), + c = m < 40.0 && s <= Sensitivity::MORE_SENSITIVE && min == 1 ? 4 : 1; + const double max = s <= Sensitivity::DEFAULT ? 12.0 : + (s <= Sensitivity::MORE_SENSITIVE ? 4.0 : 0.4); + double b = m / (18.0 / c / min + 2.0); + if (b > 4) + b = floor(b); + else if (b > 0.4) + b = floor(b * 10) / 10; + else + b = floor(b * 1000) / 1000; + if (!config.no_block_size_limit && !lin) + b = std::min(b, max); + if (s >= Sensitivity::VERY_SENSITIVE) + b = std::min(b, 2.1); + //if (s >= Sensitivity::ULTRA_SENSITIVE) + //b = std::min(b, 0.6); + return { std::max(b, 0.001), c }; +} + void print_warnings() { if (config.sensitivity >= Sensitivity::VERY_SENSITIVE || config.verbosity == 0 || config.swipe_all) return; - if (config.command != Config::blastp && config.command != Config::blastx) + if (config.command != Config::blastp && config.command != Config::blastx && config.command != Config::blastn) return; const double ram = total_ram(); unsigned b = 2, c = 4; @@ -158,15 +193,16 @@ Compressor Config::compressor() const throw std::runtime_error("Invalid compression algorithm: " + compression); } -Config::Config(int argc, const char **argv, bool check_io) +Config::Config(int argc, const char **argv, bool check_io, CommandLineParser& parser) { - Command_line_parser parser; parser.add_command("makedb", "Build DIAMOND database from a FASTA file", makedb) -#ifdef WITH_BLASTDB - .add_command("prepdb", "Prepare BLAST database for use with Diamond", prep_blast_db) -#endif + .add_command("prepdb", "Prepare BLAST or FASTA database for use with Diamond", prep_db) .add_command("blastp", "Align amino acid query sequences against a protein reference database", blastp) .add_command("blastx", "Align DNA query sequences against a protein reference database", blastx) + .add_command("cluster", "Cluster protein sequences", cluster) + .add_command("realign", "Realign clustered sequences against their centroids", CLUSTER_REALIGN) + .add_command("recluster", "Recompute clustering to fix errors", RECLUSTER) + .add_command("reassign", "Reassign clustered sequences to the closest centroid", CLUSTER_REASSIGN) .add_command("view", "View DIAMOND alignment archive (DAA) formatted file", view) .add_command("help", "Produce help message", help) .add_command("version", "Display version information", version) @@ -174,9 +210,10 @@ Config::Config(int argc, const char **argv, bool check_io) .add_command("dbinfo", "Print information about a DIAMOND database file", dbinfo) .add_command("test", "Run regression tests", regression_test) .add_command("makeidx", "Make database index", makeidx) + .add_command("greedy-vertex-cover", "Compute greedy vertex cover", GREEDY_VERTEX_COVER) .add_command("roc", "", roc) .add_command("benchmark", "", benchmark) - .add_command("merge-daa", "", MERGE_DAA) + .add_command("deepclust", "", DEEPCLUST) #ifdef EXTRA .add_command("random-seqs", "", random_seqs) .add_command("sort", "", sort) @@ -187,7 +224,6 @@ Config::Config(int argc, const char **argv, bool check_io) .add_command("info", "", info) .add_command("seed-stat", "", seed_stat) .add_command("smith-waterman", "", smith_waterman) - .add_command("cluster", "", cluster) .add_command("translate", "", translate) .add_command("filter-blasttab", "", filter_blasttab) .add_command("show-cbs", "", show_cbs) @@ -198,21 +234,100 @@ Config::Config(int argc, const char **argv, bool check_io) .add_command("reverse", "", reverse_seqs) .add_command("compute-medoids", "", compute_medoids) .add_command("mutate", "", mutate) - .add_command("merge-tsv", "", merge_tsv) .add_command("roc-id", "", rocid) .add_command("find-shapes", "", find_shapes) .add_command("composition", "", composition) .add_command("join", "", JOIN) .add_command("hashseqs", "", HASH_SEQS) .add_command("listseeds", "", LIST_SEEDS) + .add_command("index-fasta", "", INDEX_FASTA) + .add_command("fetch-seq", "", FETCH_SEQ) + .add_command("blastn", "Align DNA query sequences against a DNA reference database", blastn) + .add_command("length-sort", "", LENGTH_SORT) + .add_command("merge-daa", "", MERGE_DAA) #endif ; - Options_group general("General options"); + auto& general = parser.add_group("General options", { makedb, blastp, blastx, cluster, view, prep_db, getseq, dbinfo, makeidx, CLUSTER_REALIGN, GREEDY_VERTEX_COVER, DEEPCLUST }); general.add() ("threads", 'p', "number of CPU threads", threads_) ("db", 'd', "database file", database) ("out", 'o', "output file", output_file) + ("verbose", 'v', "verbose console output", verbose) + ("log", 0, "enable debug log", debug_log) + ("quiet", 0, "disable console output", quiet) + ("header", 0, "Use header lines in tabular output format (0/simple/verbose).", output_header, Option>(), 0); + + string dbstring; + auto& makedb_opt = parser.add_group("Makedb options", { makedb }); + makedb_opt.add() + ("in", 0, "input reference file in FASTA format", input_ref_file) + ("taxonmap", 0, "protein accession to taxid mapping file", prot_accession2taxid) + ("taxonnodes", 0, "taxonomy nodes.dmp from NCBI", nodesdmp) + ("taxonnames", 0, "taxonomy names.dmp from NCBI", namesdmp); + + auto& align_clust = parser.add_group("Aligner/Clustering options", { blastp, blastx, cluster, RECLUSTER, CLUSTER_REASSIGN, DEEPCLUST, CLUSTER_REALIGN }); + align_clust.add() + ("evalue", 'e', "maximum e-value to report alignments (default=0.001)", max_evalue, 0.001) + ("tmpdir", 't', "directory for temporary files", tmpdir) + ("comp-based-stats", 0, "composition based statistics mode (0-4)", comp_based_stats, 1u) + ("masking", 0, "masking algorithm (none, seg, tantan=default)", masking_) + ("soft-masking", 0, "soft masking", soft_masking) + ("motif-masking", 0, "softmask abundant motifs (0/1)", motif_masking) + ("approx-id", 0, "minimum approx. identity% to report an alignment/to cluster sequences", approx_min_id) + ("ext", 0, "Extension mode (banded-fast/banded-slow/full)", ext_) + ("memory-limit", 'M', "Memory limit in GB (default = 16G)", memory_limit) + ("mmseqs-compat", 0, "", mmseqs_compat) + ("no-block-size-limit", 0, "", no_block_size_limit); + + auto& aligner = parser.add_group("Aligner options", { blastp, blastx, makeidx, CLUSTER_REASSIGN }); + aligner.add() + ("query", 'q', "input query file", query_file) + ("strand", 0, "query strands to search (both/minus/plus)", query_strands, string("both")) + ("un", 0, "file for unaligned queries", unaligned) + ("al", 0, "file or aligned queries", aligned_file) + ("unfmt", 0, "format of unaligned query file (fasta/fastq)", unfmt, string("fasta")) + ("alfmt", 0, "format of aligned query file (fasta/fastq)", alfmt, string("fasta")) + ("unal", 0, "report unaligned queries (0=no, 1=yes)", report_unaligned, -1) + ("max-target-seqs", 'k', "maximum number of target sequences to report alignments for (default=25)", max_target_seqs_) + ("top", 0, "report alignments within this percentage range of top alignment score (overrides --max-target-seqs)", toppercent, 100.0) + ("max-hsps", 0, "maximum number of HSPs per target sequence to report for each query (default=1)", max_hsps, 1u) + ("range-culling", 0, "restrict hit culling to overlapping query ranges", query_range_culling) + ("compress", 0, "compression for output files (0=none, 1=gzip, zstd)", compression) + ("min-score", 0, "minimum bit score to report alignments (overrides e-value setting)", min_bit_score) + ("id", 0, "minimum identity% to report an alignment", min_id) + ("query-cover", 0, "minimum query cover% to report an alignment", query_cover) + ("subject-cover", 0, "minimum subject cover% to report an alignment", subject_cover) + ("faster", 0, "enable faster mode", mode_faster) + ("fast", 0, "enable fast mode", mode_fast) + ("mid-sensitive", 0, "enable mid-sensitive mode", mode_mid_sensitive) + ("sensitive", 0, "enable sensitive mode)", mode_sensitive) + ("more-sensitive", 0, "enable more sensitive mode", mode_more_sensitive) + ("very-sensitive", 0, "enable very sensitive mode", mode_very_sensitive) + ("ultra-sensitive", 0, "enable ultra sensitive mode", mode_ultra_sensitive) + ("swipe", 0, "exhaustive alignment against all database sequences", swipe_all) + ("iterate", 0, "iterated search with increasing sensitivity", iterate, Option>(), 0) + ("global-ranking", 'g', "number of targets for global ranking", global_ranking_targets) + ("block-size", 'b', "sequence block size in billions of letters (default=2.0)", chunk_size) + ("index-chunks", 'c', "number of chunks for index processing (default=4)", lowmem_) + ("parallel-tmpdir", 0, "directory for temporary files used by multiprocessing", parallel_tmpdir) + ("gapopen", 0, "gap open penalty", gap_open, -1) + ("gapextend", 0, "gap extension penalty", gap_extend, -1) + ("matrix", 0, "score matrix for protein alignment (default=BLOSUM62)", matrix, string("blosum62")) + ("custom-matrix", 0, "file containing custom scoring matrix", matrix_file) + ("frameshift", 'F', "frame shift penalty (default=disabled)", frame_shift) + ("long-reads", 0, "short for --range-culling --top 10 -F 15", long_reads) + ("query-gencode", 0, "genetic code to use to translate query (see user manual)", query_gencode, 1u) + ("salltitles", 0, "include full subject titles in DAA file", salltitles) + ("sallseqid", 0, "include all subject ids in DAA file", sallseqid) + ("no-self-hits", 0, "suppress reporting of identical self hits", no_self_hits) + ("taxonlist", 0, "restrict search to list of taxon ids (comma-separated)", taxonlist) + ("taxon-exclude", 0, "exclude list of taxon ids (comma-separated)", taxon_exclude) + ("seqidlist", 0, "filter the database by list of accessions", seqidlist) + ("skip-missing-seqids", 0, "ignore accessions missing in the database", skip_missing_seqids); + + auto& format = parser.add_group("Output format options", { blastp, blastx, view, CLUSTER_REALIGN }); + format.add() ("outfmt", 'f', "output format\n\ \t0 = BLAST pairwise\n\ \t5 = BLAST XML\n\ @@ -237,9 +352,11 @@ Config::Config(int argc, const char **argv, bool check_io) \tfull_sseq means Subject sequence\n\ \tevalue means Expect value\n\ \tbitscore means Bit score\n\ +\tcorrected_bitscore means Bit score corrected for edge effects\n\ \tscore means Raw score\n\ \tlength means Alignment length\n\ \tpident means Percentage of identical matches\n\ +\tapprox_pident means Approximate percentage of identical matches\n\ \tnident means Number of identical matches\n\ \tmismatch means Number of mismatches\n\ \tpositive means Number of positive - scoring matches\n\ @@ -262,87 +379,28 @@ Config::Config(int argc, const char **argv, bool check_io) \tqqual means Query quality values for the aligned part of the query\n\ \tfull_qqual means Query quality values\n\ \tqstrand means Query strand\n\ -\n\tDefault: qseqid sseqid pident length mismatch gapopen qstart qend sstart send evalue bitscore", output_format) -("verbose", 'v', "verbose console output", verbose) -("log", 0, "enable debug log", debug_log) -("quiet", 0, "disable console output", quiet) -("header", 0, "Write header lines to blast tabular format.", output_header); - - Options_group makedb("Makedb options"); - makedb.add() - ("in", 0, "input reference file in FASTA format", input_ref_file) - ("taxonmap", 0, "protein accession to taxid mapping file", prot_accession2taxid) - ("taxonnodes", 0, "taxonomy nodes.dmp from NCBI", nodesdmp) - ("taxonnames", 0, "taxonomy names.dmp from NCBI", namesdmp); - - Options_group cluster(""); - cluster.add() - ("cluster-algo", 0, "Clustering algorithm (\"multi-step\", \"mcl\")", cluster_algo) - ("cluster-steps", 0, "Clustering steps (\"fast\",\"mid-sensitive\",\"sensitive\", \"more-sensitive\", \"very-sensitive\", \"ultra-sensitive\")", cluster_steps, { "sensitive" }) - ("max-size-set", 0, "Maximum size of a set", max_size_set, (size_t) 1000) - ("external", 0, "save set of edges external", external, false) - ("cluster-similarity", 0, "Clustering similarity measure", cluster_similarity) - ("cluster-graph-file", 0, "Filename for dumping the graph or reading the graph if cluster-restart", cluster_graph_file) - ("cluster-restart", 0, "Restart clustering from dumped graph", cluster_restart) - ("mcl-expansion", 0, "MCL expansion coefficient (default=2)", cluster_mcl_expansion, (uint32_t) 2) - ("mcl-inflation", 0, "MCL inflation coefficient (default=2.0)", cluster_mcl_inflation, 2.0) - ("mcl-chunk-size", 0, "MCL chunk size per thread (default=100)", cluster_mcl_chunk_size, (uint32_t) 1) - ("mcl-max-iterations", 0, "MCL maximum iterations (default=100)", cluster_mcl_max_iter, (uint32_t) 100) - ("mcl-sparsity-switch", 0, "MCL switch to sparse matrix computation (default=0.8) ", cluster_mcl_sparsity_switch, 0.8) - ("mcl-nonsymmetric", 0, "Do not symmetrize the transistion matrix before clustering", cluster_mcl_nonsymmetric) - ("mcl-stats", 0, "Some stats about the connected components in MCL", cluster_mcl_stats); +\n\tDefault: qseqid sseqid pident length mismatch gapopen qstart qend sstart send evalue bitscore", output_format); + + auto& cluster_opt = parser.add_group("Clustering options", { cluster, RECLUSTER, CLUSTER_REASSIGN, GREEDY_VERTEX_COVER, DEEPCLUST }); + kmer_ranking = false; + cluster_opt.add() + ("member-cover", 0, "Minimum coverage% of the cluster member sequence (default=80.0)", member_cover, 80.0) + ("cluster-steps", 0, "Clustering steps", cluster_steps) + ("centroid-out", 0, "Output file for centroids (greedy vertex cover workflow)", centroid_out) +#ifdef KEEP_TARGET_ID + ("kmer-ranking", 0, "Rank sequences based on kmer frequency in linear stage", kmer_ranking) +#endif + ; - Options_group aligner("Aligner options"); - aligner.add() - ("query", 'q', "input query file", query_file) - ("strand", 0, "query strands to search (both/minus/plus)", query_strands, string("both")) - ("un", 0, "file for unaligned queries", unaligned) - ("al", 0, "file or aligned queries", aligned_file) - ("unfmt", 0, "format of unaligned query file (fasta/fastq)", unfmt, string("fasta")) - ("alfmt", 0, "format of aligned query file (fasta/fastq)", alfmt, string("fasta")) - ("unal", 0, "report unaligned queries (0=no, 1=yes)", report_unaligned, -1) - ("max-target-seqs", 'k', "maximum number of target sequences to report alignments for (default=25)", max_alignments, size_t(25)) - ("top", 0, "report alignments within this percentage range of top alignment score (overrides --max-target-seqs)", toppercent, 100.0) - ("max-hsps", 0, "maximum number of HSPs per target sequence to report for each query (default=1)", max_hsps, 1u) - ("range-culling", 0, "restrict hit culling to overlapping query ranges", query_range_culling) - ("compress", 0, "compression for output files (0=none, 1=gzip, zstd)", compression) - ("evalue", 'e', "maximum e-value to report alignments (default=0.001)", max_evalue, 0.001) - ("min-score", 0, "minimum bit score to report alignments (overrides e-value setting)", min_bit_score) - ("id", 0, "minimum identity% to report an alignment", min_id) - ("query-cover", 0, "minimum query cover% to report an alignment", query_cover) - ("subject-cover", 0, "minimum subject cover% to report an alignment", subject_cover) - ("fast", 0, "enable fast mode", mode_fast) - ("mid-sensitive", 0, "enable mid-sensitive mode", mode_mid_sensitive) - ("sensitive", 0, "enable sensitive mode)", mode_sensitive) - ("more-sensitive", 0, "enable more sensitive mode", mode_more_sensitive) - ("very-sensitive", 0, "enable very sensitive mode", mode_very_sensitive) - ("ultra-sensitive", 0, "enable ultra sensitive mode", mode_ultra_sensitive) - ("iterate", 0, "iterated search with increasing sensitivity", iterate, Option>(), 0) - ("global-ranking", 'g', "number of targets for global ranking", global_ranking_targets) - ("block-size", 'b', "sequence block size in billions of letters (default=2.0)", chunk_size) - ("index-chunks", 'c', "number of chunks for index processing (default=4)", lowmem_) - ("tmpdir", 't', "directory for temporary files", tmpdir) - ("parallel-tmpdir", 0, "directory for temporary files used by multiprocessing", parallel_tmpdir) - ("gapopen", 0, "gap open penalty", gap_open, -1) - ("gapextend", 0, "gap extension penalty", gap_extend, -1) - ("frameshift", 'F', "frame shift penalty (default=disabled)", frame_shift) - ("long-reads", 0, "short for --range-culling --top 10 -F 15", long_reads) - ("matrix", 0, "score matrix for protein alignment (default=BLOSUM62)", matrix, string("blosum62")) - ("custom-matrix", 0, "file containing custom scoring matrix", matrix_file) - ("comp-based-stats", 0, "composition based statistics mode (0-4)", comp_based_stats, 1u) - ("masking", 0, "masking algorithm (none, seg, tantan=default)", masking, string("tantan")) - ("query-gencode", 0, "genetic code to use to translate query (see user manual)", query_gencode, 1u) - ("salltitles", 0, "include full subject titles in DAA file", salltitles) - ("sallseqid", 0, "include all subject ids in DAA file", sallseqid) - ("no-self-hits", 0, "suppress reporting of identical self hits", no_self_hits) - ("taxonlist", 0, "restrict search to list of taxon ids (comma-separated)", taxonlist) - ("taxon-exclude", 0, "exclude list of taxon ids (comma-separated)", taxon_exclude) - ("seqidlist", 0, "filter the database by list of accessions", seqidlist) - ("skip-missing-seqids", 0, "ignore accessions missing in the database", skip_missing_seqids); + auto& realign_opt = parser.add_group("Cluster input options", { CLUSTER_REALIGN, RECLUSTER, CLUSTER_REASSIGN, GREEDY_VERTEX_COVER }); + realign_opt.add() + ("clusters", 0, "Clustering input file mapping sequences to centroids", clustering) + ("edges", 0, "Input file for greedy vertex cover", edges) + ("edge-format", 0, "Edge format for greedy vertex cover (default/triplet)", edge_format); string algo_str; - Options_group advanced("Advanced options"); + auto& advanced = parser.add_group("Advanced options", { blastp, blastx, makeidx, CLUSTER_REASSIGN, regression_test }); advanced.add() ("algo", 0, "Seed search algorithm (0=double-indexed/1=query-indexed/ctg=contiguous-seed)", algo_str) ("bin", 0, "number of query bins for seed search", query_bins_) @@ -350,7 +408,6 @@ Config::Config(int argc, const char **argv, bool check_io) ("seed-cut", 0, "cutoff for seed complexity", seed_cut_) ("freq-masking", 0, "mask seeds based on frequency", freq_masking) ("freq-sd", 0, "number of standard deviations for ignoring frequent seeds", freq_sd_, 0.0) - ("motif-masking", 0, "softmask abundant motifs (0/1)", motif_masking) ("id2", 0, "minimum number of identities for stage 1 hit", min_identities_) ("xdrop", 'x', "xdrop for ungapped alignment", ungapped_xdrop, 12.3) ("gapped-filter-evalue", 0, "E-value threshold for gapped filter (auto)", gapped_filter_evalue_, -1.0) @@ -363,7 +420,6 @@ Config::Config(int argc, const char **argv, bool check_io) ("mp-query-chunk", 0, "process only a single query chunk as specified", mp_query_chunk, -1) ("ext-chunk-size", 0, "chunk size for adaptive ranking (default=auto)", ext_chunk_size) ("no-ranking", 0, "disable ranking heuristic", no_ranking) - ("ext", 0, "Extension mode (banded-fast/banded-slow/full)", ext_) ("culling-overlap", 0, "minimum range overlap with higher scoring hit to delete a hit (default=50%)", inner_culling_overlap, 50.0) ("taxon-k", 0, "maximum number of targets to report per species", taxon_k, (uint64_t)0) ("range-cover", 0, "percentage of query range to be covered for range culling (default=50%)", query_range_cover, 50.0) @@ -373,10 +429,10 @@ Config::Config(int argc, const char **argv, bool check_io) ("stop-match-score", 0, "Set the match score of stop codons against each other.", stop_match_score, 1) ("tantan-minMaskProb", 0, "minimum repeat probability for masking (default=0.9)", tantan_minMaskProb, 0.9) ("file-buffer-size", 0, "file buffer size in bytes (default=67108864)", file_buffer_size, (size_t)67108864) - ("memory-limit", 'M', "Memory limit for extension stage in GB", memory_limit) ("no-unlink", 0, "Do not unlink temporary files.", no_unlink) ("target-indexed", 0, "Enable target-indexed mode", target_indexed) ("ignore-warnings", 0, "Ignore warnings", ignore_warnings) + ("unaligned-targets", 0, "", unaligned_targets) ("cut-bar", 0, "", cut_bar) ("check-multi-target", 0, "", check_multi_target) ("roc-file", 0, "", roc_file) @@ -384,26 +440,28 @@ Config::Config(int argc, const char **argv, bool check_io) ("family-map-query", 0, "", family_map_query) ("query-parallel-limit", 0, "", query_parallel_limit, 3000000u) ("log-evalue-scale", 0, "", log_evalue_scale, 1.0 / std::log(2.0)) - ("bootstrap", 0, "", bootstrap); + ("bootstrap", 0, "", bootstrap) + ("mp-self", 0, "", mp_self) + ("query-or-subject-cover", 0, "", query_or_target_cover); - Options_group view_options("View options"); + auto& view_options = parser.add_group("View options", { view, blastp, blastx }); view_options.add() ("daa", 'a', "DIAMOND alignment archive (DAA) file", daa_file) ("forwardonly", 0, "only show alignments of forward strand", forwardonly); - Options_group getseq_options("Getseq options"); + auto& getseq_options = parser.add_group("Getseq options", { getseq }); getseq_options.add() ("seq", 0, "Space-separated list of sequence numbers to display.", seq_no); double rank_ratio2, lambda, K; unsigned window, min_ungapped_score, hit_band, min_hit_score; - Options_group deprecated_options(""); + auto& deprecated_options = parser.add_group("", { blastp, blastx }); deprecated_options.add() ("window", 'w', "window size for local hit search", window) ("ungapped-score", 0, "minimum alignment score to continue local extension", min_ungapped_score) ("hit-band", 0, "band for hit verification", hit_band) ("hit-score", 0, "minimum score to keep a tentative alignment", min_hit_score) - ("gapped-xdrop", 'X', "xdrop for gapped alignment in bits", gapped_xdrop, 20) + ("gapped-xdrop", 'X', "xdrop for gapped alignment in bits", gapped_xdrop, 20.0) ("rank-ratio2", 0, "include subjects within this ratio of last hit (stage 2)", rank_ratio2, -1.0) ("rank-ratio", 0, "include subjects within this ratio of last hit", rank_ratio, -1.0) ("lambda", 0, "lambda parameter for custom matrix", lambda) @@ -414,9 +472,9 @@ Config::Config(int argc, const char **argv, bool check_io) double cbs_angle; #ifdef EXTRA - Options_group hidden_options(""); + auto& hidden_options = parser.add_group("", {}); #else - Options_group hidden_options("", true); + auto& hidden_options = parser.add_group("", {}, true); #endif hidden_options.add() ("match1", 0, "", match_file1) @@ -467,7 +525,6 @@ Config::Config(int argc, const char **argv, bool check_io) ("hard-masked", 0, "", hardmasked) ("cbs-window", 0, "", cbs_window, 40) ("no-dict", 0, "", no_dict) - ("swipe", 0, "", swipe_all) ("upgma-edge-limit", 0, "", upgma_edge_limit, (uint64_t)10000000) ("tree", 0, "", tree_file) ("upgma-dist", 0, "", upgma_dist) @@ -493,8 +550,8 @@ Config::Config(int argc, const char **argv, bool check_io) ("band-bin", 0, "", band_bin, 24) ("col-bin", 0, "", col_bin, 400) ("self", 0, "", self) - ("trace-pt-fetch-size", 0, "", trace_pt_fetch_size, (size_t)10e9) - ("tile-size", 0, "", tile_size, (size_t)1024) + ("trace-pt-fetch-size", 0, "", trace_pt_fetch_size, (int64_t)10e9) + ("tile-size", 0, "", tile_size, (uint32_t)1024) ("short-query-ungapped-bitscore", 0, "", short_query_ungapped_bitscore, 25.0) ("short-query-max-len", 0, "", short_query_max_len, 60) ("gapped-filter-evalue1", 0, "", gapped_filter_evalue1, 2000.0) @@ -537,12 +594,67 @@ Config::Config(int argc, const char **argv, bool check_io) ("key2", 0, "", key2) ("motif-mask-file", 0, "", motif_mask_file) ("max-motif-len", 0, "", max_motif_len, 30) - ("chaining-stacked-hsp-ratio", 0, "", chaining_stacked_hsp_ratio, 0.5); + ("chaining-stacked-hsp-ratio", 0, "", chaining_stacked_hsp_ratio, 0.5) + ("swipe-task-size", 0, "", swipe_task_size, (int64_t)100000000) + ("minimizer-window", 0, "", minimizer_window_) + ("lin-stage1", 0, "", lin_stage1) + ("min_task_trace_pts", 0, "", min_task_trace_pts, (int64_t)1024) + ("sketch-size", 0, "", sketch_size) + ("oid-list", 0, "", oid_list) + ("bootstrap-block", 0, "", bootstrap_block, (int64_t)1000000) + ("centroid-factor", 0, "", centroid_factor, (int64_t)3) + ("timeout", 0, "", timeout) + ("resume", 0, "", resume) + ("target_hard_cap", 0, "", target_hard_cap) + ("mapany", 0, "", mapany) + ("neighbors", 0, "", neighbors) + ("reassign-overlap", 0, "", reassign_overlap, 0.3) + ("reassign-ratio", 0, "", reassign_ratio, 0.5) + ("reassign-max", 0, "", reassign_max) + ("add-self-aln", 0, "", add_self_aln) + ("weighted-gvc", 0, "", weighted_gvc) + ("filter-kmer-len", 0, "", filter_kmer_len) + ("filter-kmer-cutoff", 0, "", filter_kmer_cutoff) + ("hamming-ext", 0, "", hamming_ext) + ("diag-filter-id", 0, "", diag_filter_id) + ("diag-filter-cov", 0, "", diag_filter_cov) + ("strict-gvc", 0, "", strict_gvc) + ("dbtype", 0, "type of sequences in database file (nucl/prot)", dbstring, string("prot")) + ("penalty", 0, "blastn mismatch penalty", mismatch_penalty, -3) + ("reward", 0, "blastn match reward", match_reward, 2) + ("cluster-similarity", 0, "Clustering similarity measure (default=\"normalized_bitscore_global\")", cluster_similarity) + ("cluster-threshold", 0, "Threshold for the similarity measure (default=50%)", cluster_threshold) + ("cluster-graph-file", 0, "Filename for dumping the graph or reading the graph if cluster-restart", cluster_graph_file) + ("cluster-restart", 0, "Restart clustering from dumped graph", cluster_restart) + ("mcl-expansion", 0, "MCL expansion coefficient (default=2)", cluster_mcl_expansion, (uint32_t)2) + ("mcl-inflation", 0, "MCL inflation coefficient (default=2.0)", cluster_mcl_inflation, 2.0) + ("mcl-chunk-size", 0, "MCL chunk size per thread (default=100)", cluster_mcl_chunk_size, (uint32_t)1) + ("mcl-max-iterations", 0, "MCL maximum iterations (default=100)", cluster_mcl_max_iter, (uint32_t)100) + ("mcl-sparsity-switch", 0, "MCL switch to sparse matrix computation (default=0.8) ", cluster_mcl_sparsity_switch, 0.8) + ("mcl-nonsymmetric", 0, "Do not symmetrize the transistion matrix before clustering", cluster_mcl_nonsymmetric) + ("mcl-stats", 0, "Some stats about the connected components in MCL", cluster_mcl_stats) + ("cluster-algo", 0, "Clustering algorithm (\"mcl\")", cluster_algo) + ("approx-backtrace", 0, "", approx_backtrace) + ("prefix-scan", 0, "", prefix_scan) + ("narrow-band-cov", 0, "", narrow_band_cov) + ("narrow-band-factor", 0, "", narrow_band_factor) + ("anchor-window", 0, "", anchor_window, 12) + ("anchor-score", 0, "", anchor_score, 1.0) + ("classic-band", 0, "", classic_band) + ("no_8bit_extension", 0, "", no_8bit_extension) + ("anchored-swipe", 0, "", anchored_swipe) + ("no_chaining_merge_hsps", 0, "", no_chaining_merge_hsps) + ("no_recluster_bd", 0, "", no_recluster_bd) + ("pipeline-short", 0, "", pipeline_short) + ("graph-algo", 0, "", graph_algo, string("gvc")) +#ifndef KEEP_TARGET_ID + ("kmer-ranking", 0, "Rank sequences based on kmer frequency in linear stage", kmer_ranking) +#endif + ; - parser.add(general).add(makedb).add(cluster).add(aligner).add(advanced).add(view_options).add(getseq_options).add(hidden_options).add(deprecated_options); parser.store(argc, argv, command); - if (toppercent != 100.0 && max_alignments != 25) + if (toppercent != 100.0 && max_target_seqs_.present()) throw std::runtime_error("--top and --max-target-seqs are mutually exclusive."); if (command == blastx && no_self_hits) @@ -581,6 +693,7 @@ Config::Config(int argc, const char **argv, bool check_io) break; case Config::blastp: case Config::blastx: + case Config::blastn: if (database == "") throw std::runtime_error("Missing parameter: database file (--db/-d)"); if (daa_file.length() > 0) { @@ -614,7 +727,7 @@ Config::Config(int argc, const char **argv, bool check_io) verbosity = 0; else if (verbose) verbosity = 2; - else if (((command == Config::view || command == blastx || command == blastp) && output_file == "") + else if (((command == Config::view || command == blastx || command == blastp || command == blastn) && output_file == "") || command == Config::version || command == getseq || command == fastq2fasta || command == regression_test) verbosity = 0; else @@ -622,15 +735,15 @@ Config::Config(int argc, const char **argv, bool check_io) switch (verbosity) { case 0: - message_stream = Message_stream(false); + message_stream = MessageStream(false); break; case 3: - log_stream = Message_stream(true, !config.no_logfile); - verbose_stream = Message_stream(true, !config.no_logfile); - message_stream = Message_stream(true, !config.no_logfile); + log_stream = MessageStream(true, !config.no_logfile); + verbose_stream = MessageStream(true, !config.no_logfile); + message_stream = MessageStream(true, !config.no_logfile); break; case 2: - verbose_stream = Message_stream(); + verbose_stream = MessageStream(); default: ; } @@ -659,16 +772,21 @@ Config::Config(int argc, const char **argv, bool check_io) #ifndef NDEBUG verbose_stream << "Assertions enabled." << endl; #endif - set_option(threads_, std::thread::hardware_concurrency()); + set_option(threads_, (int)std::thread::hardware_concurrency()); switch (command) { case Config::makedb: case Config::blastp: case Config::blastx: + case Config::blastn: case Config::view: case Config::cluster: + case Config::DEEPCLUST: case Config::regression_test: case Config::compute_medoids: + case Config::CLUSTER_REASSIGN: + case Config::GREEDY_VERTEX_COVER: + case Config::RECLUSTER: message_stream << "#CPU threads: " << threads_ << endl; default: ; @@ -676,22 +794,26 @@ Config::Config(int argc, const char **argv, bool check_io) switch (command) { case Config::blastp: - case Config::blastx: + case Config::blastx: case Config::benchmark: case Config::model_sim: case Config::opt: case Config::mask: case Config::makedb: case Config::cluster: + case Config::DEEPCLUST: case Config::regression_test: case Config::compute_medoids: case Config::LIST_SEEDS: + case Config::CLUSTER_REASSIGN: + case Config::CLUSTER_REALIGN: + case Config::RECLUSTER: if (frame_shift != 0 && command == Config::blastp) throw std::runtime_error("Frameshift alignments are only supported for translated searches."); if (query_range_culling && frame_shift == 0) throw std::runtime_error("Query range culling is only supported in frameshift alignment mode (option -F)."); if (matrix_file == "") { - score_matrix = ScoreMatrix(to_upper_case(matrix), gap_open, gap_extend, frame_shift, stop_match_score, 0, cbs_matrix_scale); + score_matrix = ScoreMatrix(to_upper_case(matrix), gap_open, gap_extend, frame_shift, stop_match_score, 0, cbs_matrix_scale, mmseqs_compat); } else { if (gap_open == -1 || gap_extend == -1) @@ -702,23 +824,13 @@ Config::Config(int argc, const char **argv, bool check_io) throw std::runtime_error("This value for --comp-based-stats is not supported when using a custom scoring matrix."); score_matrix = ScoreMatrix(matrix_file, gap_open, gap_extend, stop_match_score, ScoreMatrix::Custom()); } - if(command == Config::cluster){ - if(!Workflow::Cluster::ClusterRegistry::has(cluster_algo)){ - ostream &header_out = command == Config::help ? cout : cerr; - header_out << "Unkown clustering algorithm: " << cluster_algo << endl; - header_out << "Available options are: " << endl; - for(string c_algo : Workflow::Cluster::ClusterRegistry::getKeys()){ - header_out << "\t" << c_algo << "\t"<< Workflow::Cluster::ClusterRegistry::get(c_algo)->get_description() << endl; - } - throw std::runtime_error("Clustering algorithm not found."); - } - } message_stream << "Scoring parameters: " << score_matrix << endl; Masking::instance = unique_ptr(new Masking(score_matrix)); } - if (command == Config::blastp || command == Config::blastx || command == Config::benchmark || command == Config::model_sim || command == Config::opt - || command == Config::mask || command == Config::cluster || command == Config::compute_medoids || command == Config::regression_test) { + if (command == Config::blastp || command == Config::blastx || command == Config::blastn || command == Config::benchmark || command == Config::model_sim || command == Config::opt + || command == Config::mask || command == Config::cluster || command == Config::compute_medoids || command == Config::regression_test || command == Config::CLUSTER_REASSIGN + || command == Config::RECLUSTER || command == Config::DEEPCLUST) { if (tmpdir == "") tmpdir = extract_dir(output_file); @@ -733,6 +845,7 @@ Config::Config(int argc, const char **argv, bool check_io) } sensitivity = Sensitivity::DEFAULT; + if (mode_faster) set_sens(Sensitivity::FASTER); if (mode_fast) set_sens(Sensitivity::FAST); if (mode_mid_sensitive) set_sens(Sensitivity::MID_SENSITIVE); if (mode_sensitive) set_sens(Sensitivity::SENSITIVE); @@ -741,12 +854,13 @@ Config::Config(int argc, const char **argv, bool check_io) if (mode_ultra_sensitive) set_sens(Sensitivity::ULTRA_SENSITIVE); algo = from_string(algo_str); - + dbtype = from_string(dbstring); Translator::init(query_gencode); - if (command == blastx) + if (command == blastx || command == blastn) input_value_traits = nucleotide_traits; + if (command == help) parser.print_help(); @@ -806,6 +920,9 @@ Config::Config(int argc, const char **argv, bool check_io) #endif #ifdef STRICT_BAND log_stream << " STRICT_BAND"; +#endif +#ifdef KEEP_TARGET_ID + log_stream << " KEEP_TARGET_ID"; #endif log_stream << endl; } diff --git a/src/basic/config.h b/src/basic/config.h index ad4882bd5..fcabdf16c 100644 --- a/src/basic/config.h +++ b/src/basic/config.h @@ -29,7 +29,7 @@ along with this program. If not, see . #include "../util/options/option.h" #include "value.h" -enum class Sensitivity { FAST = 0, DEFAULT = 1, MID_SENSITIVE = 2, SENSITIVE = 3, MORE_SENSITIVE = 4, VERY_SENSITIVE = 5, ULTRA_SENSITIVE = 6 }; +enum class Sensitivity { FASTER = -1, FAST = 0, DEFAULT = 1, MID_SENSITIVE = 2, SENSITIVE = 3, MORE_SENSITIVE = 4, VERY_SENSITIVE = 5, ULTRA_SENSITIVE = 6}; enum class Compressor; template<> struct EnumTraits { @@ -37,6 +37,21 @@ template<> struct EnumTraits { static const SEMap from_string; }; +template<> struct EnumTraits { + static const EMap to_string; + static const SEMap from_string; +}; + +enum class GraphAlgo { GREEDY_VERTEX_COVER, LEN_SORTED }; + +template<> struct EnumTraits { + static const SEMap from_string; +}; + +struct CommandLineParser; + +constexpr int64_t DEFAULT_MAX_TARGET_SEQS = 25; + struct Config { @@ -44,12 +59,12 @@ struct Config using string_vector = std::vector; string_vector input_ref_file; - unsigned threads_; - string database; + int threads_; + Option database; string_vector query_file; unsigned merge_seq_treshold; unsigned shapes; - size_t max_alignments; + Option max_target_seqs_; string match_file1; string match_file2; int padding; @@ -70,17 +85,18 @@ struct Config string tmpdir; string parallel_tmpdir; bool long_mode; - int gapped_xdrop; + double gapped_xdrop; double max_evalue; string kegg_file; int gap_open; int gap_extend; + int mismatch_penalty; + int match_reward; string matrix; bool debug_log, verbose, quiet; bool salltitles; int reward; int penalty; - string db_type; double min_id; unsigned compress_temp; double toppercent; @@ -91,6 +107,7 @@ struct Config unsigned fetch_size; uint64_t db_size; double query_cover; + double query_or_target_cover; bool mode_sensitive; unsigned verbosity; bool no_auto_append; @@ -129,7 +146,7 @@ struct Config string prot_accession2taxid; int superblock; unsigned max_cells; - string masking; + Option masking_; bool log_query; bool log_subject; unsigned threads_align; @@ -159,7 +176,7 @@ struct Config unsigned swipe_chunk_size; unsigned query_parallel_limit; bool long_reads; - bool output_header; + Option output_header; string alfmt; string unfmt; string namesdmp; @@ -205,8 +222,8 @@ struct Config int col_bin; size_t file_buffer_size; bool self; - size_t trace_pt_fetch_size; - size_t tile_size; + int64_t trace_pt_fetch_size; + uint32_t tile_size; double short_query_ungapped_bitscore; int short_query_max_len; double gapped_filter_evalue1; @@ -222,8 +239,7 @@ struct Config size_t chaining_min_nodes; bool fast_tsv; unsigned target_parallel_verbosity; - double memory_limit; - size_t global_ranking_targets; + int64_t global_ranking_targets; bool mode_mid_sensitive; bool no_ranking; bool query_memory; @@ -269,6 +285,60 @@ struct Config bool freq_masking; Loc max_motif_len; double chaining_stacked_hsp_ratio; + Option cluster_threshold; + Option memory_limit; + int64_t swipe_task_size; + Loc minimizer_window_; + bool lin_stage1; + int64_t min_task_trace_pts; + Loc sketch_size; + string soft_masking; + string oid_list; + int64_t bootstrap_block; + int64_t centroid_factor; + int timeout; + string resume; + int64_t target_hard_cap; + bool mapany; + Option clustering; + Option neighbors; + double reassign_overlap; + double reassign_ratio; + int64_t reassign_max; + bool add_self_aln; + string centroid_out; + string unaligned_targets; + Option approx_min_id; + bool mode_faster; + double member_cover; + bool weighted_gvc; + int filter_kmer_len; + double filter_kmer_cutoff; + bool kmer_ranking; + bool hamming_ext; + double diag_filter_id; + double diag_filter_cov; + bool strict_gvc; + bool mmseqs_compat; + string edge_format; + bool no_block_size_limit; + string edges; + bool mp_self; + bool approx_backtrace; + bool prefix_scan; + double narrow_band_cov; + double narrow_band_factor; + Loc anchor_window; + double anchor_score; + bool classic_band; + bool no_8bit_extension; + bool anchored_swipe; + bool no_chaining_merge_hsps; + bool no_recluster_bd; + bool pipeline_short; + string graph_algo; + + SequenceType dbtype; Sensitivity sensitivity; @@ -281,15 +351,16 @@ struct Config makedb = 0, blastp = 1, blastx = 2, view = 3, help = 4, version = 5, getseq = 6, benchmark = 7, random_seqs = 8, compare = 9, sort = 10, roc = 11, db_stat = 12, model_sim = 13, match_file_stat = 14, model_seqs = 15, opt = 16, mask = 17, fastq2fasta = 18, dbinfo = 19, test_extra = 20, test_io = 21, db_annot_stats = 22, read_sim = 23, info = 24, seed_stat = 25, smith_waterman = 26, cluster = 27, translate = 28, filter_blasttab = 29, show_cbs = 30, simulate_seqs = 31, split = 32, upgma = 33, upgma_mc = 34, regression_test = 35, - reverse_seqs = 36, compute_medoids = 37, mutate = 38, merge_tsv = 39, rocid = 40, makeidx = 41, find_shapes, prep_blast_db, composition, JOIN, HASH_SEQS, LIST_SEEDS, MERGE_DAA + reverse_seqs = 36, compute_medoids = 37, mutate = 38, rocid = 40, makeidx = 41, find_shapes, prep_db, composition, JOIN, HASH_SEQS, LIST_SEEDS, CLUSTER_REALIGN, + GREEDY_VERTEX_COVER, INDEX_FASTA, FETCH_SEQ, CLUSTER_REASSIGN, blastn, RECLUSTER, LENGTH_SORT, MERGE_DAA, DEEPCLUST }; unsigned command; enum class Algo { AUTO = -1, DOUBLE_INDEXED = 0, QUERY_INDEXED = 1, CTG_SEED }; Algo algo; - string cluster_algo; - string cluster_similarity; + Option cluster_algo; + Option cluster_similarity; string cluster_graph_file; bool cluster_restart; @@ -308,10 +379,9 @@ struct Config unsigned load_balancing; Config() {} - Config(int argc, const char **argv, bool check_io = true); + Config(int argc, const char **argv, bool check_io, CommandLineParser& parser); - inline unsigned get_run_len(unsigned length) - { + Loc min_orf_len(Loc length) const { if (run_len == 0) { if (length < 30 || frame_shift != 0) return 1; @@ -324,12 +394,16 @@ struct Config return run_len; } - inline bool output_range(unsigned n_target_seq, int score, int top_score) + inline bool output_range(unsigned n_target_seq, int score, int top_score, const int64_t max_target_seqs) { if (toppercent < 100) return (1.0 - (double)score / top_score) * 100 <= toppercent; else - return n_target_seq < max_alignments; + return n_target_seq < max_target_seqs; + } + + int64_t block_size() const { + return (int64_t)(chunk_size * 1e9); } void set_sens(Sensitivity sens); @@ -356,3 +430,7 @@ template<> struct EnumTraits { static const EMap to_string; static const SEMap from_string; }; + +extern const char* const DEFAULT_MEMORY_LIMIT; + +std::pair block_size(int64_t memory_limit, Sensitivity s, bool lin); diff --git a/src/basic/const.h b/src/basic/const.h index 013f31a80..e04491203 100644 --- a/src/basic/const.h +++ b/src/basic/const.h @@ -25,7 +25,7 @@ struct Const { enum { - build_version = 153, + build_version = 154, #ifdef SINGLE_THREADED seedp_bits = 0, #else diff --git a/src/basic/hssp.cpp b/src/basic/hssp.cpp index 6c18994fd..a9f9d2ad8 100644 --- a/src/basic/hssp.cpp +++ b/src/basic/hssp.cpp @@ -23,8 +23,11 @@ along with this program. If not, see . #include "../align/align.h" #include "../output/output.h" #include "../output/output_format.h" +#include "../stats/stats.h" using std::list; +using std::equal; +using std::runtime_error; std::pair Hsp::diagonal_bounds() const { @@ -45,13 +48,15 @@ bool Hsp::is_weakly_enveloped(const Hsp &j) const && query_range.overlap_factor(j.query_range) >= overlap_factor; } -HspContext& HspContext::parse() +HspContext& HspContext::parse(const OutputFormat* output_format) { - if (!flag_any(output_format->hsp_values, HspValues::TRANSCRIPT) && config.command != Config::view) { + if (output_format && !flag_any(output_format->hsp_values, HspValues::TRANSCRIPT) && config.command != Config::view) { hsp_.query_source_range = TranslatedPosition::absolute_interval( TranslatedPosition(hsp_.query_range.begin_, Frame(hsp_.frame)), TranslatedPosition(hsp_.query_range.end_, Frame(hsp_.frame)), (int)query.source().length()); + if (subject_seq.length() > 0) + hsp_.approx_id = hsp_.approx_id_percent(this->query.index(hsp_.frame), subject_seq); return *this; } @@ -91,11 +96,13 @@ HspContext& HspContext::parse() hsp_.query_range.end_ = i.query_pos.translated; hsp_.subject_range.end_ = i.subject_pos; hsp_.query_source_range = TranslatedPosition::absolute_interval(begin().query_pos, i.query_pos, (int)query.source().length()); + if (subject_seq.length() > 0) + hsp_.approx_id = hsp_.approx_id_percent(this->query.index(hsp_.frame), subject_seq); return *this; } -void Hsp::push_back(const DiagonalSegment &d, const TranslatedSequence &query, const Sequence &subject, bool reversed) +void Hsp::push_back(const DiagonalSegmentT &d, const TranslatedSequence &query, const Sequence &subject, bool reversed) { const Sequence &q = query[d.i.frame]; if (reversed) { @@ -134,7 +141,7 @@ void Hsp::push_back(const DiagonalSegment &d, const TranslatedSequence &query, c } } -void Hsp::splice(const DiagonalSegment &a, const DiagonalSegment &b, const TranslatedSequence &query, const Sequence &subject, bool reversed) +void Hsp::splice(const DiagonalSegmentT &a, const DiagonalSegmentT &b, const TranslatedSequence &query, const Sequence &subject, bool reversed) { TranslatedPosition i0 = a.query_last(); int j0 = a.subject_last(); @@ -164,7 +171,7 @@ void Hsp::splice(const DiagonalSegment &a, const DiagonalSegment &b, const Trans } } -void Hsp::set_begin(const DiagonalSegment &d, int dna_len) +void Hsp::set_begin(const DiagonalSegmentT &d, int dna_len) { subject_range.begin_ = d.j; query_range.begin_ = d.i; @@ -175,7 +182,7 @@ void Hsp::set_begin(const DiagonalSegment &d, int dna_len) query_source_range.end_ = d.i.absolute(dna_len) + 1; } -void Hsp::set_end(const DiagonalSegment &d, int dna_len) +void Hsp::set_end(const DiagonalSegmentT &d, int dna_len) { subject_range.end_ = d.subject_end(); query_range.end_ = d.query_end(); @@ -279,16 +286,17 @@ void Hsp::push_gap(Edit_operation op, int length, const Letter *subject) #endif } -Hsp::Hsp(const IntermediateRecord &r, unsigned query_source_len) : +Hsp::Hsp(const IntermediateRecord &r, unsigned query_source_len, Loc qlen, Loc tlen, const OutputFormat* output_format) : backtraced(!IntermediateRecord::stats_mode(output_format->hsp_values) && output_format->hsp_values != HspValues::NONE), score(r.score), evalue(r.evalue), bit_score(score_matrix.bitscore(r.score)), + corrected_bit_score(score_matrix.bitscore_corrected(r.score, qlen, tlen)), transcript(r.transcript) { subject_range.begin_ = r.subject_begin; - if (align_mode.mode == Align_mode::blastx) { - frame = (r.flag&(1 << 6)) == 0 ? r.query_begin % 3 : 3 + (query_source_len - 1 - r.query_begin) % 3; + if (align_mode.mode == AlignMode::blastx) { + frame = r.frame(query_source_len, align_mode.mode); set_translated_query_begin(r.query_begin, query_source_len); } else { @@ -302,10 +310,57 @@ Hsp::Hsp(const IntermediateRecord &r, unsigned query_source_len) : mismatches = r.mismatches; positives = r.positives; length = r.length; - if (align_mode.mode == Align_mode::blastx) + if (align_mode.mode == AlignMode::blastx) set_translated_query_end(r.query_end, query_source_len); else query_range.end_ = r.query_end + 1; subject_range.end_ = r.subject_end; } +} + +Hsp::Hsp(const ApproxHsp& h, Loc qlen, Loc tlen) : + backtraced(true), + score(h.score), + frame(0), + length(h.query_range.length()), + identities(length), + mismatches(0), + positives(length), + gap_openings(0), + gaps(0), + swipe_target(0), + d_begin(0), + d_end(0), + query_source_range(h.query_range), + query_range(h.query_range), + subject_range(h.subject_range), + evalue(h.evalue), + bit_score(score_matrix.bitscore(h.score)), + corrected_bit_score(score_matrix.bitscore_corrected(h.score, qlen, tlen)), + approx_id(100.0) +{ +} + +bool Hsp::is_identity(const Sequence& query, const Sequence& target) const { + query_range.check(query.length()); + subject_range.check(target.length()); + return query_range.length() == subject_range.length() + && std::equal(query.data() + query_range.begin_, query.data() + query_range.end_, target.data() + subject_range.begin_, + [](Letter x, Letter y) {return letter_mask(x) == letter_mask(y); }); +} + +double Hsp::approx_id_percent(const Sequence& query, const Sequence& target) const { + return is_identity(query, target) ? 100.0 : Stats::approx_id(score, query_range.length(), subject_range.length()); +} + +double HspContext::qcovhsp() const { + return (double)query_source_range().length() * 100.0 / query_len; +} + +double HspContext::scovhsp() const { + return (double)subject_range().length() * 100.0 / subject_len; +} + +double HspContext::id_percent() const { + return (double)identities() * 100 / length(); } \ No newline at end of file diff --git a/src/basic/match.h b/src/basic/match.h index d5e836a77..fb05d84ab 100644 --- a/src/basic/match.h +++ b/src/basic/match.h @@ -24,21 +24,27 @@ along with this program. If not, see . #pragma once #include #include +#include #include "sequence.h" #include "packed_loc.h" #include "value.h" #include "packed_transcript.h" #include "translated_position.h" -#include "diagonal_segment.h" +#include "../util/geo/diagonal_segment.h" +#include "value.h" +#include "../util/io/serialize.h" +#include "../util/io/input_file.h" +#include "../util/hsp/approx_hsp.h" -inline interval normalized_range(unsigned pos, int len, Strand strand) +inline Interval normalized_range(unsigned pos, int len, Strand strand) { return strand == FORWARD - ? interval (pos, pos + len) - : interval (pos + 1 + len, pos + 1); + ? Interval(pos, pos + len) + : Interval(pos + 1 + len, pos + 1); } struct IntermediateRecord; +struct OutputFormat; namespace Stats { struct TargetMatrix; @@ -60,6 +66,10 @@ struct Hsp swipe_target(0), d_begin(0), d_end(0), + evalue(DBL_MAX), + bit_score(0.0), + corrected_bit_score(0.0), + approx_id(0.0), matrix(nullptr) {} @@ -76,10 +86,15 @@ struct Hsp swipe_target(swipe_target), d_begin(0), d_end(0), + evalue(DBL_MAX), + bit_score(0.0), + corrected_bit_score(0.0), + approx_id(0.0), matrix(nullptr) {} - Hsp(const IntermediateRecord &r, unsigned query_source_len); + Hsp(const IntermediateRecord &r, unsigned query_source_len, Loc qlen, Loc tlen, const OutputFormat* output_format); + Hsp(const ApproxHsp& h, Loc qlen, Loc tlen); struct Iterator { @@ -144,12 +159,12 @@ struct Hsp return Iterator(*this); } - interval oriented_range() const + Interval oriented_range() const { if (frame < 3) - return interval(query_source_range.begin_, query_source_range.end_ - 1); + return Interval(query_source_range.begin_, query_source_range.end_ - 1); else - return interval(query_source_range.end_ - 1, query_source_range.begin_); + return Interval(query_source_range.end_ - 1, query_source_range.begin_); } void set_translated_query_begin(unsigned oriented_query_begin, unsigned dna_len) @@ -210,7 +225,7 @@ struct Hsp return int((1 - overlap)*score); } - bool envelopes(const DiagonalSegment &d, int dna_len) const + bool envelopes(const DiagonalSegmentT &d, int dna_len) const { return query_source_range.contains(d.query_absolute_range(dna_len)) || subject_range.contains(d.subject_range()); } @@ -220,22 +235,24 @@ struct Hsp bool query_range_enveloped_by(const Hsp& hsp, double p) const; bool query_range_enveloped_by(std::list::const_iterator begin, std::list::const_iterator end, double p) const; bool is_weakly_enveloped_by(std::list::const_iterator begin, std::list::const_iterator end, int cutoff) const; - void push_back(const DiagonalSegment &d, const TranslatedSequence &query, const Sequence& subject, bool reversed); + void push_back(const DiagonalSegmentT &d, const TranslatedSequence &query, const Sequence& subject, bool reversed); void push_match(Letter q, Letter s, bool positive); void push_gap(Edit_operation op, int length, const Letter *subject); - void splice(const DiagonalSegment &d0, const DiagonalSegment &d1, const TranslatedSequence &query, const Sequence& subject, bool reversed); - void set_begin(const DiagonalSegment &d, int dna_len); - void set_end(const DiagonalSegment &d, int dna_len); + void splice(const DiagonalSegmentT &d0, const DiagonalSegmentT &d1, const TranslatedSequence &query, const Sequence& subject, bool reversed); + void set_begin(const DiagonalSegmentT &d, int dna_len); + void set_end(const DiagonalSegmentT &d, int dna_len); void set_begin(int i, int j, Frame frame, int dna_len); void set_end(int i, int j, Frame frame, int dna_len); void clear(); + double approx_id_percent(const Sequence& query, const Sequence& target) const; + bool is_identity(const Sequence& query, const Sequence& target) const; bool is_weakly_enveloped(const Hsp &j) const; std::pair diagonal_bounds() const; bool backtraced; - int score, frame, length, identities, mismatches, positives, gap_openings, gaps, swipe_target, d_begin, d_end; - interval query_source_range, query_range, subject_range; - double evalue, bit_score; + int score, frame, length, identities, mismatches, positives, gap_openings, gaps, swipe_target, d_begin, d_end, reserved1, reserved2; + Interval query_source_range, query_range, subject_range; + double evalue, bit_score, corrected_bit_score, approx_id; Sequence target_seq; const Stats::TargetMatrix* matrix; Packed_transcript transcript; @@ -243,16 +260,19 @@ struct Hsp struct HspContext { + HspContext() + {} HspContext( - Hsp& hsp, - unsigned query_id, + const Hsp& hsp, + BlockId query_id, + OId query_oid, const TranslatedSequence &query, const char *query_title, - unsigned subject_oid, + OId subject_oid, unsigned subject_len, const char* subject_title, - unsigned hit_num, - unsigned hsp_num, + int hit_num, + int hsp_num, const Sequence &subject_seq, int ungapped_score = 0, const double query_self_aln_score = 0.0, @@ -261,7 +281,9 @@ struct HspContext query_title(query_title), target_title(subject_title), query_id(query_id), + query_oid(query_oid), subject_oid(subject_oid), + query_len(query.source().length()), subject_len(subject_len), hit_num(hit_num), hsp_num(hsp_num), @@ -346,6 +368,12 @@ struct HspContext double bit_score() const { return hsp_.bit_score; } + double corrected_bit_score() const { + return hsp_.corrected_bit_score; + } + double approx_id() const { + return hsp_.approx_id; + } unsigned frame() const { return hsp_.frame; } unsigned length() const @@ -360,28 +388,119 @@ struct HspContext { return hsp_.gap_openings; } unsigned gaps() const { return hsp_.gaps; } - const interval& query_source_range() const + double approx_id_percent() const { + return hsp_.approx_id_percent(query.index(hsp_.frame), subject_seq); + } + double qcovhsp() const; + double scovhsp() const; + double id_percent() const; + const Interval& query_source_range() const { return hsp_.query_source_range; } - const interval& query_range() const + const Interval& query_range() const { return hsp_.query_range; } - const interval& subject_range() const + const Interval& subject_range() const { return hsp_.subject_range; } - interval oriented_query_range() const + Interval oriented_query_range() const { return hsp_.oriented_range(); } int blast_query_frame() const { return hsp_.blast_query_frame(); } Packed_transcript transcript() const { return hsp_.transcript; } - HspContext& parse(); + bool operator<(const HspContext& h) const { + return query_oid < h.query_oid; + } + int reserved1() const { + return hsp_.reserved1; + } + int reserved2() const { + return hsp_.reserved2; + } + HspContext& parse(const OutputFormat* output_format); - const TranslatedSequence query; - const char* query_title, *target_title; - const unsigned query_id, subject_oid; - const Loc subject_len; - const unsigned hit_num, hsp_num; + TranslatedSequence query; + std::string query_title, target_title; + BlockId query_id; + OId query_oid, subject_oid; + Loc query_len, subject_len; + unsigned hit_num, hsp_num; int ungapped_score; - const double query_self_aln_score, target_self_aln_score; - const Sequence subject_seq; + double query_self_aln_score, target_self_aln_score; + Sequence subject_seq; private: - Hsp &hsp_; + Hsp hsp_; + template friend struct TypeDeserializer; }; + +template<> +struct TypeSerializer { + + TypeSerializer(TextBuffer& buf) : + buf_(&buf) + {} + + TypeSerializer& operator<<(const HspContext& h) { + buf_->write(h.query_id); + buf_->write(h.query_oid); + buf_->write(h.subject_oid); + buf_->write_c_str(h.query_title.c_str()); + buf_->write_c_str(h.target_title.c_str()); + buf_->write(h.query_len); + buf_->write(h.subject_len); + buf_->write(h.identities()); + buf_->write(h.mismatches()); + buf_->write(h.gaps()); + buf_->write(h.length()); + buf_->write(h.gap_openings()); + buf_->write(h.query_range().begin_); + buf_->write(h.query_range().end_); + buf_->write(h.subject_range().begin_); + buf_->write(h.subject_range().end_); + buf_->write(h.bit_score()); + buf_->write(h.evalue()); + buf_->write(h.approx_id()); + return *this; + } + +private: + + TextBuffer* buf_; + +}; + +template<> +struct TypeDeserializer { + + TypeDeserializer(InputFile* f) : + file_(f) + {} + + HspContext get() { + HspContext h; + file_->read(h.query_id); + file_->read(h.query_oid); + file_->read(h.subject_oid); + *file_ >> h.query_title; + *file_ >> h.target_title; + file_->read(h.query_len); + file_->read(h.subject_len); + file_->read(h.hsp_.identities); + file_->read(h.hsp_.mismatches); + file_->read(h.hsp_.gaps); + file_->read(h.hsp_.length); + file_->read(h.hsp_.gap_openings); + file_->read(h.hsp_.query_range.begin_); + file_->read(h.hsp_.query_range.end_); + file_->read(h.hsp_.subject_range.begin_); + file_->read(h.hsp_.subject_range.end_); + file_->read(h.hsp_.bit_score); + file_->read(h.hsp_.evalue); + file_->read(h.hsp_.approx_id); + h.hsp_.query_source_range = h.hsp_.query_range; + return h; + } + +private: + + InputFile* file_; + +}; \ No newline at end of file diff --git a/src/basic/packed_loc.h b/src/basic/packed_loc.h index 94f6c967b..cc743d2eb 100644 --- a/src/basic/packed_loc.h +++ b/src/basic/packed_loc.h @@ -51,9 +51,15 @@ struct packed_uint40_t operator uint32_t() const { return low; } + operator int32_t() const { + return low; + } bool operator==(const packed_uint40_t& rhs) const { return high == rhs.high && low == rhs.low; } + bool operator!=(const packed_uint40_t& rhs) const { + return high != rhs.high && low != rhs.low; + } bool operator<(const packed_uint40_t &rhs) const { return high < rhs.high || (high == rhs.high && low < rhs.low); } friend uint64_t operator-(const packed_uint40_t &x, const packed_uint40_t &y) diff --git a/src/basic/packed_sequence.h b/src/basic/packed_sequence.h index 1eed4d577..48b2f132c 100644 --- a/src/basic/packed_sequence.h +++ b/src/basic/packed_sequence.h @@ -16,16 +16,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef PACKED_SEQUENCE_H_ -#define PACKED_SEQUENCE_H_ - +#pragma once #include #include "value.h" #include "../util/binary_buffer.h" #include "sequence.h" -using std::vector; - inline bool has_n(const Sequence &seq) { for(Loc i=0;i(seq); else pack<2>(seq); break; - case amino_acid: + case SequenceType::amino_acid: pack<5>(seq); } } @@ -59,7 +55,7 @@ struct Packed_sequence it.read(data_, l); } - void unpack(vector &dst, unsigned b, unsigned len) + void unpack(std::vector &dst, unsigned b, unsigned len) { dst.clear(); unsigned x = 0, n = 0, l = 0; @@ -76,7 +72,7 @@ struct Packed_sequence } } - const vector& data() const + const std::vector& data() const { return data_; } bool has_n() const @@ -102,8 +98,6 @@ struct Packed_sequence } bool has_n_; - vector data_; - -}; + std::vector data_; -#endif /* PACKED_SEQUENCE_H_ */ +}; \ No newline at end of file diff --git a/src/basic/packed_transcript.h b/src/basic/packed_transcript.h index 53b8e66c2..4e39e077d 100644 --- a/src/basic/packed_transcript.h +++ b/src/basic/packed_transcript.h @@ -16,12 +16,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef PACKED_TRANSCRIPT_H_ -#define PACKED_TRANSCRIPT_H_ - +#pragma once #include "../util/binary_buffer.h" #include "../basic/value.h" -#include "diagonal_segment.h" #include "sequence.h" typedef enum { op_match = 0, op_insertion = 1, op_deletion = 2, op_substitution = 3, op_frameshift_forward = 4, op_frameshift_reverse = 5 } Edit_operation; @@ -144,7 +141,7 @@ struct Packed_transcript Const_iterator begin() const { return Const_iterator (data_.data()); } - const vector& data() const + const std::vector& data() const { return data_; } const Packed_operation* ptr() const @@ -219,10 +216,8 @@ struct Packed_transcript private: - vector data_; + std::vector data_; friend struct Hsp; }; - -#endif /* PACKED_TRANSCRIPT_H_ */ diff --git a/src/basic/reduction.h b/src/basic/reduction.h index 348a1fcb2..e748a6d68 100644 --- a/src/basic/reduction.h +++ b/src/basic/reduction.h @@ -27,7 +27,6 @@ along with this program. If not, see . #include #include "value.h" #include "sequence.h" -#include "translate.h" struct Reduction { @@ -39,7 +38,7 @@ struct Reduction return size_; } - uint64_t bit_size() const + int bit_size() const { return bit_size_; } @@ -73,14 +72,14 @@ struct Reduction for (unsigned i = 0; i < r.size_; ++i) { os << '['; for (unsigned j = 0; j < 20; ++j) - if (r. map_[j] == i) + if (r.map_[j] == i) os << value_traits.alphabet[j]; os << ']'; } return os; } - static void reduce_seq(const Sequence &seq, vector &dst) + static void reduce_seq(const Sequence &seq, std::vector &dst) { dst.clear(); dst.resize(seq.length()); @@ -101,7 +100,7 @@ struct Reduction unsigned map_[256]; alignas(16) Letter map8_[256], map8b_[256]; unsigned size_; - uint64_t bit_size_; + int bit_size_; double bit_size_exact_; std::array freq_; diff --git a/src/basic/seed.h b/src/basic/seed.h index 6181f1a7f..e416cef61 100644 --- a/src/basic/seed.h +++ b/src/basic/seed.h @@ -16,9 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef SEED_H_ -#define SEED_H_ - +#pragma once #include #include "const.h" #include "../util/hash_function.h" @@ -26,16 +24,21 @@ along with this program. If not, see . #include "value.h" #include "../stats/score_matrix.h" -typedef uint64_t Packed_seed; +using PackedSeed = uint64_t; +#ifdef LONG_SEEDS +using SeedOffset = uint64_t; +#else +using SeedOffset = uint32_t; +#endif -inline unsigned seed_partition(Packed_seed s) +inline unsigned seed_partition(PackedSeed s) { return (unsigned)(s & (Const::seedp-1)); } -inline unsigned seed_partition_offset(Packed_seed s) +inline SeedOffset seed_partition_offset(PackedSeed s) { - return (unsigned)(s >> Const::seedp_bits); + return (SeedOffset)(s >> Const::seedp_bits); } struct Hashed_seed @@ -43,7 +46,7 @@ struct Hashed_seed Hashed_seed() {} explicit Hashed_seed(uint64_t seed): - hash(murmur_hash()(seed)) + hash(MurmurHash()(seed)) {} unsigned partition() const { @@ -97,4 +100,3 @@ struct Seed Letter data_[Const::max_seed_weight]; }; -#endif /* SEED_H_ */ diff --git a/src/basic/seed_iterator.h b/src/basic/seed_iterator.h index 21383e605..b1094b8b5 100644 --- a/src/basic/seed_iterator.h +++ b/src/basic/seed_iterator.h @@ -20,6 +20,7 @@ along with this program. If not, see . ****/ #pragma once +#include #include "shape.h" #include "sequence.h" #include "../util/hash_function.h" @@ -27,7 +28,7 @@ along with this program. If not, see . struct SeedIterator { - SeedIterator(vector &seq, const Shape &sh): + SeedIterator(std::vector &seq, const Shape &sh): ptr_ (seq.data()), end_ (ptr_ + seq.size() - sh.length_ + 1) {} @@ -47,6 +48,114 @@ struct SeedIterator const Letter *ptr_, *end_; }; +struct MinimizerIterator +{ + MinimizerIterator(std::vector& seq, const Shape& sh, Loc window) : + ptr_(seq.data()), + begin_(seq.data()), + end_(ptr_ + seq.size() - sh.length_ + 1), + window_(window), + sh_(sh) + { + next(); + if (good()) + min_idx_ = get(); + } + bool good() const + { + return (Loc)seeds_.size() == window_; + } + uint64_t operator*() const { + return seeds_[min_idx_]; + } + MinimizerIterator& operator++() { + int m = 0; + const uint64_t current = **this; + do { + seeds_.pop_front(); + hashes_.pop_front(); + pos_.pop_front(); + next(); + } while (good() && seeds_[m = get()] == current); + min_idx_ = m; + return *this; + } + Loc pos() const { + return pos_[min_idx_]; + } +private: + void next() { + while ((Loc)seeds_.size() < window_ && ptr_ < end_) { + uint64_t s; + if (sh_.set_seed_reduced(s, ptr_)) { + seeds_.push_back(s); + hashes_.push_back(MurmurHash()(s)); + pos_.push_back(Loc(ptr_ - begin_)); + } + ++ptr_; + } + } + int get() const { + std::deque::const_iterator i = hashes_.begin(), j = i; + uint64_t s = *i++; + while (i < hashes_.end()) { + if (*i < s) { + s = *i; + j = i; + } + ++i; + } + return int(j - hashes_.begin()); + } + const Letter* ptr_, *begin_, *end_; + std::deque seeds_, hashes_; + std::deque pos_; + const Loc window_; + const Shape& sh_; + int min_idx_; +}; + +struct SketchIterator +{ + SketchIterator(std::vector& seq, const Shape& sh, Loc n) + { + std::vector v; + v.reserve(seq.size() - sh.length_ + 1); + const Letter* end = seq.data() + seq.size() - sh.length_ + 1; + uint64_t s; + for (const Letter* p = seq.data(); p < end; ++p) + if (sh.set_seed_reduced(s, p)) + v.emplace_back(s, MurmurHash()(s), Loc(p - seq.data())); + std::sort(v.begin(), v.end()); + data_.insert(data_.end(), v.begin(), v.begin() + std::min(n, (Loc)v.size())); + it_ = data_.begin(); + } + bool good() const { + return it_ < data_.end(); + } + uint64_t operator*() const { + return it_->seed; + } + Loc pos() const { + return it_->pos; + } + SketchIterator& operator++() { + ++it_; + return *this; + } +private: + struct Kmer { + Kmer(uint64_t seed, uint64_t hash, Loc pos) : seed(seed), hash(hash), pos(pos) {} + uint64_t seed, hash; + Loc pos; + bool operator<(const Kmer& k) const { + return hash < k.hash; + } + }; + std::vector data_; + std::vector::const_iterator it_; +}; + template struct HashedSeedIterator { @@ -70,7 +179,7 @@ struct HashedSeedIterator if (!is_amino_acid(l)) return false; last_ |= Reduction::reduction(l); - seed = murmur_hash()(last_ & mask); + seed = MurmurHash()(last_ & mask); return true; } private: diff --git a/src/basic/sequence.h b/src/basic/sequence.h index 56a2b05fe..a865aac29 100644 --- a/src/basic/sequence.h +++ b/src/basic/sequence.h @@ -29,7 +29,7 @@ along with this program. If not, see . #include "../util/binary_buffer.h" #include "../util/text_buffer.h" #include "translated_position.h" -#include "../util/interval.h" +#include "../util/geo/interval.h" struct Sequence { @@ -56,7 +56,7 @@ struct Sequence len_(Loc(end - begin)), data_(begin) {} - Sequence(const vector &data): + Sequence(const std::vector &data): len_((Loc)data.size()), data_(data.data()) {} @@ -162,6 +162,12 @@ struct Sequence { return Sequence(*this, begin, end - 1); } + Sequence subseq(int begin) const { + return Sequence(data_ + begin, len_ - begin); + } + Sequence subseq_clipped(int begin, int end) const { + return Sequence(*this, std::max(begin, 0), std::min(end, len_)); + } friend TextBuffer& operator<<(TextBuffer &buf, const Sequence &s) { return s.print(buf, value_traits); @@ -189,7 +195,7 @@ struct Sequence return std::vector(data_, data_ + len_); } std::vector reverse() const; - void mask(const interval &i) { + void mask(const Interval &i) { for (int j = i.begin_; j < i.end_; ++j) ((Letter*)data_)[j] = value_traits.mask_char; } @@ -201,7 +207,17 @@ struct Sequence return false; return true; } - static vector from_string(const char* str, const ValueTraits&vt = value_traits); + Loc masked_letters() const { + Loc n = 0; + for (Loc i = 0; i < len_; ++i) + if (letter_mask(data_[i]) == MASK_LETTER) + ++n; + return n; + } + double masked_letter_ratio() const { + return (double)masked_letters() / len_; + } + static std::vector from_string(const char* str, const ValueTraits&vt = value_traits); Loc len_; const Letter *data_; @@ -230,7 +246,7 @@ struct TranslatedSequence translated_[5] = s6; } - TranslatedSequence(const Sequence&source, const vector v[6]): + TranslatedSequence(const Sequence&source, const std::vector v[6]): source_(source) { for (int i = 0; i < 6; ++i) diff --git a/src/basic/shape.h b/src/basic/shape.h index 97d969e73..315fe4ce7 100644 --- a/src/basic/shape.h +++ b/src/basic/shape.h @@ -28,7 +28,6 @@ along with this program. If not, see . #include "../stats/score_matrix.h" #include "reduction.h" #include "config.h" -#include "translate.h" struct Shape { @@ -70,7 +69,7 @@ struct Shape d_ = positions_[weight_/2-1]; } - inline bool set_seed(Packed_seed &s, const Letter *seq) const + inline bool set_seed(PackedSeed &s, const Letter *seq) const { s = 0; #ifdef FREQUENCY_MASKING @@ -96,7 +95,7 @@ struct Shape return true; } - inline bool set_seed_shifted(Packed_seed &s, const Letter *seq) const + inline bool set_seed_shifted(PackedSeed &s, const Letter *seq) const { s = 0; const uint64_t b = Reduction::reduction.bit_size(); @@ -114,7 +113,7 @@ struct Shape return true; } - inline bool set_seed_reduced(Packed_seed &s, const Letter *seq) const + inline bool set_seed_reduced(PackedSeed &s, const Letter *seq) const { s = 0; for (int i = 0; i < weight_; ++i) { diff --git a/src/basic/statistics.h b/src/basic/statistics.h index ed77d69ad..3f9eeedae 100644 --- a/src/basic/statistics.h +++ b/src/basic/statistics.h @@ -34,10 +34,11 @@ struct Statistics enum value { SEED_HITS, TENTATIVE_MATCHES0, TENTATIVE_MATCHES1, TENTATIVE_MATCHES2, TENTATIVE_MATCHES3, TENTATIVE_MATCHES4, TENTATIVE_MATCHESX, MATCHES, ALIGNED, GAPPED, DUPLICATES, GAPPED_HITS, QUERY_SEEDS, QUERY_SEEDS_HIT, REF_SEEDS, REF_SEEDS_HIT, QUERY_SIZE, REF_SIZE, OUT_HITS, OUT_MATCHES, COLLISION_LOOKUPS, QCOV, BIAS_ERRORS, SCORE_TOTAL, ALIGNED_QLEN, PAIRWISE, HIGH_SIM, - SEARCH_TEMP_SPACE, SECONDARY_HITS, ERASED_HITS, SQUARED_ERROR, CELLS, TARGET_HITS0, TARGET_HITS1, TARGET_HITS2, TARGET_HITS3, TARGET_HITS3_CBS, TARGET_HITS4, TARGET_HITS5, TIME_GREEDY_EXT, LOW_COMPLEXITY_SEEDS, + SEARCH_TEMP_SPACE, SECONDARY_HITS, ERASED_HITS, SQUARED_ERROR, CELLS, TARGET_HITS0, TARGET_HITS1, TARGET_HITS2, TARGET_HITS3, TARGET_HITS3_CBS, TARGET_HITS4, TARGET_HITS5, TARGET_HITS6, TIME_GREEDY_EXT, LOW_COMPLEXITY_SEEDS, SWIPE_REALIGN, EXT8, EXT16, EXT32, GAPPED_FILTER_TARGETS, GAPPED_FILTER_HITS1, GAPPED_FILTER_HITS2, GROSS_DP_CELLS, NET_DP_CELLS, TIME_TARGET_SORT, TIME_SW, TIME_EXT, TIME_GAPPED_FILTER, TIME_LOAD_HIT_TARGETS, TIME_CHAINING, TIME_LOAD_SEED_HITS, TIME_SORT_SEED_HITS, TIME_SORT_TARGETS_BY_SCORE, TIME_TARGET_PARALLEL, TIME_TRACEBACK_SW, TIME_TRACEBACK, HARD_QUERIES, TIME_MATRIX_ADJUST, - MATRIX_ADJUST_COUNT, MASKED_LAZY, COUNT + MATRIX_ADJUST_COUNT, MASKED_LAZY, SWIPE_TASKS_TOTAL, SWIPE_TASKS_ASYNC, TRIVIAL_ALN, TIME_EXT_32, EXT_OVERFLOW_8, EXT_WASTED_16, DP_CELLS_8, DP_CELLS_16, DP_CELLS_32, TIME_PROFILE, TIME_ANCHORED_SWIPE, + TIME_ANCHORED_SWIPE_ALLOC, TIME_ANCHORED_SWIPE_SORT, TIME_ANCHORED_SWIPE_ADD, TIME_ANCHORED_SWIPE_OUTPUT, COUNT }; Statistics() diff --git a/src/basic/translated_position.h b/src/basic/translated_position.h index d8d077b6b..2bd059e02 100644 --- a/src/basic/translated_position.h +++ b/src/basic/translated_position.h @@ -16,11 +16,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef TRANSLATED_POSITION_H_ -#define TRANSLATED_POSITION_H_ - +#pragma once #include -#include "../util/interval.h" +#include "../util/geo/interval.h" #include "value.h" enum Strand { FORWARD = 0, REVERSE = 1 }; @@ -127,12 +125,12 @@ struct TranslatedPosition return oriented_position(in_strand(), frame.strand, dna_len); } - static interval absolute_interval(const TranslatedPosition &begin, const TranslatedPosition &end, int dna_len) + static Interval absolute_interval(const TranslatedPosition &begin, const TranslatedPosition &end, int dna_len) { if (begin.frame.strand == FORWARD) - return interval(begin.in_strand(), end.in_strand()); + return Interval(begin.in_strand(), end.in_strand()); else - return interval(oriented_position(end.in_strand() - 1, REVERSE, dna_len), oriented_position(begin.in_strand() - 1, REVERSE, dna_len)); + return Interval(oriented_position(end.in_strand() - 1, REVERSE, dna_len), oriented_position(begin.in_strand() - 1, REVERSE, dna_len)); } static int in_strand_to_translated(int in_strand) @@ -183,5 +181,3 @@ struct TranslatedPosition int translated; }; - -#endif \ No newline at end of file diff --git a/src/basic/value.h b/src/basic/value.h index 423021e44..4e74edc13 100644 --- a/src/basic/value.h +++ b/src/basic/value.h @@ -27,10 +27,11 @@ along with this program. If not, see . #include "../util/simd.h" typedef signed char Letter; -typedef enum { amino_acid=0, nucleotide=1 } SequenceType; +enum class SequenceType : int32_t { amino_acid=0, nucleotide=1 }; struct Amino_acid {}; struct Nucleotide {}; + struct invalid_sequence_char_exception : public std::exception { const std::string msg; @@ -92,13 +93,21 @@ static inline Letter letter_mask(Letter x) { #ifdef __SSE2__ static inline __m128i letter_mask(__m128i x) { - return _mm_and_si128(x, SIMD::_mm_set1_epi8(LETTER_MASK)); +#ifdef SEQ_MASK + return _mm_and_si128(x, _mm_set1_epi8(LETTER_MASK)); +#else + return x; +#endif } #endif #ifdef __AVX2__ static inline __m256i letter_mask(__m256i x) { - return _mm256_and_si256(x, ::SIMD::_mm256_set1_epi8(LETTER_MASK)); +#ifdef SEQ_MASK + return _mm256_and_si256(x, _mm256_set1_epi8(LETTER_MASK)); +#else + return x; +#endif } #endif @@ -112,9 +121,9 @@ static inline char to_char(Letter a) return value_traits.alphabet[(long)a]; } -struct Align_mode +struct AlignMode { - Align_mode(unsigned mode); + AlignMode(unsigned mode); static unsigned from_command(unsigned command); int check_context(int i) const { @@ -128,7 +137,7 @@ struct Align_mode bool query_translated; }; -extern Align_mode align_mode; +extern AlignMode align_mode; extern const Letter IUPACAA_TO_STD[32]; extern const Letter NCBI_TO_STD[28]; @@ -144,4 +153,11 @@ void alph_ncbi_to_std(const It begin, const It end) { } } -using Loc = int32_t; \ No newline at end of file +using Loc = int32_t; +using BlockId = int32_t; +using OId = int64_t; +using DictId = int64_t; +using Score = int32_t; +using TaxId = int32_t; +using CentroidId = OId; +using SuperBlockId = int32_t; \ No newline at end of file diff --git a/src/chaining/aligner.h b/src/chaining/aligner.h new file mode 100644 index 000000000..1ea5ba905 --- /dev/null +++ b/src/chaining/aligner.h @@ -0,0 +1,34 @@ +#pragma once +#include "chaining.h" +#include "diag_graph.h" +#include "../basic/match.h" + +namespace Chaining { + +struct Aligner +{ + + enum { link_padding = 10, reverse_link_min_overhang = 10 }; + + int get_approximate_link(int d_idx, int e_idx, double space_penalty, int max_i); + template + void forward_pass(_it begin, _it end, bool init, double space_penalty); + void backtrace(const size_t node, const int j_end, Hsp* out, ApproxHsp& t, const int score_max, const int score_min, const int max_shift, unsigned& next) const; + bool backtrace_old(size_t node, int j_end, Hsp* out, ApproxHsp& t, int score_max, int score_min, int max_shift, unsigned& next) const; + void backtrace(size_t top_node, Hsp* out, ApproxHsp& t, int max_shift, unsigned& next, int max_j) const; + int backtrace(size_t top_node, std::list& hsps, std::list& ts, std::list::iterator& t_begin, int cutoff, int max_shift) const; + int backtrace(std::list& hsps, std::list& ts, int cutoff, int max_shift) const; + int run(std::list& hsps, std::list& ts, double space_penalty, int cutoff, int max_shift); + int run(std::list& hsps, std::list& ts, std::vector::const_iterator begin, std::vector::const_iterator end, int band); + Aligner(const Sequence& query, const Sequence& subject, bool log, unsigned frame); + + const Sequence query, subject; + //const Bias_correction &query_bc; + const bool log; + const unsigned frame; + static thread_local DiagGraph diags; + static thread_local std::map window; + +}; + +} \ No newline at end of file diff --git a/src/chaining/backtrace.cpp b/src/chaining/backtrace.cpp new file mode 100644 index 000000000..bf1a58211 --- /dev/null +++ b/src/chaining/backtrace.cpp @@ -0,0 +1,340 @@ +#include +#include "aligner.h" +#include "../basic/config.h" +#include "../util/geo/geo.h" + +using std::vector; +using std::cout; +using std::endl; +using std::list; +using std::min; +using std::max; +using std::numeric_limits; + +namespace Chaining { + +static bool disjoint(list::const_iterator begin, list::const_iterator end, const ApproxHsp &t, int cutoff) +{ + for (; begin != end; ++begin) { + const double ot = t.subject_range.overlap_factor(begin->subject_range), + oq = t.query_range.overlap_factor(begin->query_range); + if ((1.0 - min(ot, oq)) * t.score / begin->score >= config.chaining_stacked_hsp_ratio) + continue; + if ((1.0 - max(ot, oq)) * t.score < cutoff) + return false; + //if (begin->partial_score(t) < cutoff || !begin->collinear(t)) + //if (!begin->disjoint(t) || !begin->collinear(t)) + //if (!begin->rel_disjoint(t)) + // return false; + } + return true; +} + +static bool disjoint(list::const_iterator begin, list::const_iterator end, const DiagonalSegment &d, int cutoff) +{ + for (; begin != end; ++begin) { + const double ot = d.subject_range().overlap_factor(begin->subject_range), + oq = d.query_range().overlap_factor(begin->query_range); + if ((1.0 - min(ot, oq)) * d.score / begin->score >= config.chaining_stacked_hsp_ratio) + continue; + if ((1.0 - max(ot, oq)) * d.score < cutoff) + return false; + //if (begin->partial_score(d) < cutoff || !begin->collinear(d)) + //if (!begin->disjoint(d) || !begin->collinear(d)) + //if (!begin->rel_disjoint(d)) + //return false; + } + return true; +} + +bool Aligner::backtrace_old(size_t node, int j_end, Hsp* out, ApproxHsp& t, int score_max, int score_min, int max_shift, unsigned& next) const +{ + + const DiagonalNode& d = diags[node]; + vector::const_iterator f = diags.get_edge(node, j_end); + bool at_end = f >= diags.edges.end(); + const int prefix_score = at_end ? d.score : f->prefix_score; + if (prefix_score > score_max) + return false; + + int j; + score_min = std::min(score_min, at_end ? 0 : f->prefix_score_begin); + + //if (f != diags.edges.end() && (!stop_at_min || f->path_min == diags[f->node_out].path_min)) { + if (!at_end) { + const DiagonalNode& e = diags[f->node_out]; + const int shift = d.diag() - e.diag(); + j = f->j; + + if (abs(shift) <= max_shift) { + const bool bt = backtrace_old(f->node_out, shift > 0 ? j : j + shift, out, t, score_max, score_min, max_shift, next); + if (!bt) { + if (f->prefix_score_begin > score_min) + return false; + else + at_end = true; + } + } + else { + next = f->node_out; + at_end = true; + } + } + + if (at_end) { + if (out) { + out->query_range.begin_ = d.i; + out->subject_range.begin_ = d.j; + out->score = score_max - score_min; + } + t.query_range.begin_ = d.i; + t.subject_range.begin_ = d.j; + t.score = score_max - score_min; + j = d.j; + } + else { + const DiagonalNode& e = diags[f->node_out]; + const int shift = d.diag() - e.diag(); + if (out) { + if (shift > 0) { + out->transcript.push_back(op_insertion, (unsigned)shift); + out->length += shift; + } + else if (shift < 0) { + for (int j2 = j + shift; j2 < j; ++j2) { + out->transcript.push_back(op_deletion, subject[j2]); + ++out->length; + } + } + } + } + + const int dd = d.diag(); + t.d_max = std::max(t.d_max, dd); + t.d_min = std::min(t.d_min, dd); + if (d.score > t.max_diag.score) { + t.max_diag = d; + t.max_diag.prefix_score = prefix_score; + t.max_diag.d_max_left = max(max(t.max_diag.d_max_right, t.max_diag.d_max_left), dd); + t.max_diag.d_min_left = min(min(t.max_diag.d_min_right, t.max_diag.d_min_left), dd); + t.max_diag.d_max_right = dd; + t.max_diag.d_min_right = dd; + } + else { + t.max_diag.d_max_right = std::max(t.max_diag.d_max_right, dd); + t.max_diag.d_min_right = std::min(t.max_diag.d_min_right, dd); + } + + if (out) { + const int d2 = d.diag(); + if (log) cout << "Backtrace node=" << node << " i=" << d2 + j << "-" << d2 + j_end << " j=" << j << "-" << j_end << endl; + for (; j < j_end; ++j) { + const Letter s = subject[j], q = query[d2 + j]; + if (s == q) { + out->transcript.push_back(op_match); + ++out->identities; + } + else + out->transcript.push_back(op_substitution, s); + ++out->length; + } + } + return true; + +} + + + +void Aligner::backtrace(const size_t node, const int j_end, Hsp* out, ApproxHsp& t, const int score_max, const int score_min, const int max_shift, unsigned& next) const +{ + struct Node { + size_t node; + int score_min, j_end; + }; + vector nodes; + nodes.push_back({ node, score_min, j_end }); + bool ret = false, ret_value; + + while (!nodes.empty()) { + Node& c = nodes.back(); + const DiagonalNode& d = diags[c.node]; + vector::const_iterator f = diags.get_edge(c.node, c.j_end); + bool at_end = f >= diags.edges.end(); + const int prefix_score = at_end ? d.score : f->prefix_score; + if (!ret && prefix_score > score_max) { + ret = true; + ret_value = false; + nodes.pop_back(); + continue; + } + + c.score_min = std::min(c.score_min, at_end ? 0 : f->prefix_score_begin); + + //if (f != diags.edges.end() && (!stop_at_min || f->path_min == diags[f->node_out].path_min)) { + if (!at_end) { + const DiagonalNode& e = diags[f->node_out]; + const int shift = d.diag() - e.diag(); + const int j = f->j; + + if (abs(shift) <= max_shift) { + if (!ret) { + nodes.push_back({ f->node_out, c.score_min, shift > 0 ? j : j + shift }); + continue; + } + assert(ret == true); + if (!ret_value) { + if (f->prefix_score_begin > c.score_min) { + ret_value = false; + nodes.pop_back(); + continue; + } + else + at_end = true; + } + } + else { + next = f->node_out; + at_end = true; + } + } + + if (at_end) { + if (out) { + out->query_range.begin_ = d.i; + out->subject_range.begin_ = d.j; + out->score = score_max - c.score_min; + } + t.query_range.begin_ = d.i; + t.subject_range.begin_ = d.j; + t.score = score_max - c.score_min; + } + else { + const DiagonalNode& e = diags[f->node_out]; + const int shift = d.diag() - e.diag(); + if (out) { + if (shift > 0) { + out->transcript.push_back(op_insertion, (unsigned)shift); + out->length += shift; + } + else if (shift < 0) { + const int j = f->j; + for (int j2 = j + shift; j2 < j; ++j2) { + out->transcript.push_back(op_deletion, subject[j2]); + ++out->length; + } + } + } + } + + const int dd = d.diag(); + t.d_max = std::max(t.d_max, dd); + t.d_min = std::min(t.d_min, dd); + + if (out) { + int j = at_end ? d.j : f->j; + const int d2 = d.diag(); + if (log) cout << "Backtrace node=" << node << " i=" << d2 + j << "-" << d2 + j_end << " j=" << j << "-" << j_end << endl; + for (; j < j_end; ++j) { + const Letter s = subject[j], q = query[d2 + j]; + if (s == q) { + out->transcript.push_back(op_match); + ++out->identities; + } + else + out->transcript.push_back(op_substitution, s); + ++out->length; + } + } + ret = true; + ret_value = true; + nodes.pop_back(); + } +} + +void Aligner::backtrace(size_t top_node, Hsp* out, ApproxHsp& t, int max_shift, unsigned& next, int max_j) const +{ + ApproxHsp traits(frame); + if (top_node != DiagGraph::end) { + const DiagonalNode& d = diags[top_node]; + if (out) { + out->transcript.clear(); + out->query_range.end_ = d.query_end(); + out->subject_range.end_ = d.subject_end(); + } + traits.subject_range.end_ = d.subject_end(); + traits.query_range.end_ = d.query_end(); + int score_min = d.prefix_score; + backtrace_old(top_node, std::min(d.subject_end(), max_j), out, traits, d.prefix_score, score_min, max_shift, next); + } + else { + traits.score = 0; + if (out) + out->score = 0; + } + if (out) + out->transcript.push_terminator(); + t = traits; +} + +int Aligner::backtrace(size_t top_node, list& hsps, list& ts, list::iterator& t_begin, int cutoff, int max_shift) const +{ + unsigned next; + int max_score = 0, max_j = (int)subject.length(); + do { + Hsp* hsp = log ? new Hsp(true) : 0; + ApproxHsp t(frame); + next = std::numeric_limits::max(); + backtrace(top_node, hsp, t, max_shift, next, max_j); + //assert(t.score > 0); + //Geo::assert_diag_bounds(t.d_max, query.length(), subject.length()); + //Geo::assert_diag_bounds(t.d_min, query.length(), subject.length()); + if (t.score > 0) + max_j = t.subject_range.begin_; + if (t.score >= cutoff && disjoint(t_begin, ts.end(), t, cutoff)) { + if (t_begin == ts.end()) { + ts.push_back(t); + t_begin = ts.end(); + t_begin--; + } + else + ts.push_back(t); + if (hsp) + hsps.push_back(*hsp); + max_score = std::max(max_score, t.score); + } + delete hsp; + top_node = next; + } while (next != std::numeric_limits::max()); + return max_score; +} + +int Aligner::backtrace(list& hsps, list& ts, int cutoff, int max_shift) const +{ + vector top_nodes; + for (size_t i = 0; i < diags.nodes.size(); ++i) { + DiagonalNode& d = diags.nodes[i]; + //cout << "node=" << i << " prefix_score=" << d.prefix_score << " path_max=" << d.path_max << " rel_score=" << d.rel_score() << " cutoff=" << cutoff << endl; + //if (d.prefix_score >= cutoff && (d.prefix_score == d.path_max || d.prefix_score - d.path_min >= cutoff)) + if (d.rel_score() >= cutoff) + top_nodes.push_back(&d); + } + std::sort(top_nodes.begin(), top_nodes.end(), DiagonalNode::cmp_rel_score); + int max_score = 0; + list::iterator t_begin = ts.end(); + + for (vector::const_iterator i = top_nodes.begin(); i < top_nodes.end(); ++i) { + const size_t node = *i - diags.nodes.data(); + if (log) + cout << "Backtrace candidate node=" << node << endl; + if (disjoint(t_begin, ts.end(), **i, cutoff)) { + if (log) + cout << "Backtrace node=" << node << " prefix_score=" << (*i)->prefix_score << " rel_score=" << (*i)->rel_score() << endl; + max_score = std::max(max_score, backtrace(node, hsps, ts, t_begin, cutoff, max_shift)); + if (log) + cout << endl; + } + } + return max_score; +} + +} \ No newline at end of file diff --git a/src/chaining/chaining.h b/src/chaining/chaining.h index 02b9a7d72..50ea247ae 100644 --- a/src/chaining/chaining.h +++ b/src/chaining/chaining.h @@ -20,180 +20,13 @@ along with this program. If not, see . #include #include #include -#include "../dp/hsp_traits.h" -#include "../stats/hauser_correction.h" +#include "../util/hsp/approx_hsp.h" +#include "../dp/dp.h" -std::pair> greedy_align(Sequence query, Sequence subject, std::vector::const_iterator begin, std::vector::const_iterator end, bool log, unsigned frame); +namespace Chaining { -struct Diagonal_node : public Diagonal_segment -{ - enum { estimate, finished }; - Diagonal_node() : - Diagonal_segment(), - link_idx(-1), - prefix_score(0), - path_max(0), - path_min(0) - {} - Diagonal_node(int query_pos, int subject_pos, int len, int score, int link_idx = -1) : - Diagonal_segment(query_pos, subject_pos, len, score), - link_idx(link_idx), - prefix_score(score), - path_max(score), - path_min(score) - {} - Diagonal_node(const Diagonal_segment &d) : - Diagonal_segment(d), - link_idx(-1), - prefix_score(d.score), - path_max(d.score), - path_min(d.score) - {} - void deactivate() - { - link_idx = 0; - } - void reset() - { - link_idx = -1; - prefix_score = score; - path_max = score; - path_min = score; - } - bool is_maximum() const - { - return path_max == prefix_score; - } - int rel_score() const - { - return prefix_score == path_max ? prefix_score : prefix_score - path_min; - } - static bool cmp_prefix_score(const Diagonal_node *x, const Diagonal_node *y) - { - return x->prefix_score > y->prefix_score; - } - static bool cmp_rel_score(const Diagonal_node *x, const Diagonal_node *y) - { - return x->rel_score() > y->rel_score(); - } - int link_idx, prefix_score, path_max, path_min; -}; +std::pair> run(Sequence query, Sequence subject, std::vector::const_iterator begin, std::vector::const_iterator end, bool log, unsigned frame); +std::list run(Sequence query, const std::vector& targets); +ApproxHsp hamming_ext(std::vector::iterator begin, std::vector::iterator end, Loc qlen, Loc tlen); -struct Diag_graph -{ - - enum { end = size_t(-1) }; - - struct Edge - { - Edge() : - prefix_score(0), - node_in() - { - } - Edge(int prefix_score, int path_max, int j, unsigned node_in, unsigned node_out, int path_min, int prefix_score_begin) : - prefix_score(prefix_score), - path_max(path_max), - j(j), - path_min(path_min), - prefix_score_begin(prefix_score_begin), - node_in(node_in), - node_out(node_out) - { - } - /*operator int() const - { - return prefix_score; - } - bool operator<(const Edge &x) const - { - return prefix_score > x.prefix_score; - }*/ - int prefix_score, path_max, j, path_min, prefix_score_begin; - unsigned node_in, node_out; - }; - - void init() - { - nodes.clear(); - edges.clear(); - } - - void init(unsigned node) - { - if (edges.size() >= (size_t)std::numeric_limits::max()) - throw std::runtime_error("Too many edges."); - nodes[node].link_idx = (int)edges.size(); - } - - void load(vector::const_iterator begin, vector::const_iterator end); - void sort(); - void prune(); - void clear_edges(); - - vector::iterator add_edge(const Edge &edge) - { - for (vector::iterator j = nodes.begin() + edge.node_in + 1; j < nodes.end(); ++j) - if (j->link_idx == -1) - break; - else - ++j->link_idx; - assert(nodes[edge.node_in].link_idx >= 0 && nodes[edge.node_in].link_idx <= (int)edges.size()); - Diagonal_node &d = nodes[edge.node_in]; - if (edge.prefix_score > d.prefix_score) { - d.prefix_score = edge.prefix_score; - d.path_max = edge.path_max; - d.path_min = edge.path_min; - } - return edges.insert(edges.begin() + d.link_idx++, edge); - } - - vector::const_iterator get_edge(size_t node, int j) const - { - const Diagonal_node &d = nodes[node]; - if (d.score == 0) - return edges.begin() + d.link_idx - 1; - if (edges.empty()) - return edges.end(); - int max_score = d.score; - ptrdiff_t max_i = -1; - for (ptrdiff_t i = d.link_idx - 1; i >= 0 && edges[i].node_in == node; --i) - if (edges[i].j < j && edges[i].prefix_score > max_score) { - max_i = i; - max_score = edges[i].prefix_score; - } - - /*for (vector::const_iterator i = edges.begin() + d.link_idx - 1; i >= edges.begin() && i->node_in == node; --i) - if (i->j < j && i->prefix_score > max_score) { - max_edge = i; - max_score = i->prefix_score; - }*/ - return max_i >= 0 ? edges.begin() + max_i : edges.end(); - } - - int prefix_score(size_t node, int j, int &path_max, int &path_min) const - { - const vector::const_iterator i = get_edge(node, j); - path_max = i == edges.end() ? nodes[node].score : std::max(nodes[node].score, i->path_max); - path_min = i == edges.end() ? nodes[node].score : i->path_min; - return i == edges.end() ? nodes[node].score : std::max(nodes[node].score, i->prefix_score); - } - - Diagonal_node& operator[](size_t k) - { - return nodes[k]; - } - - const Diagonal_node& operator[](size_t k) const - { - return nodes[k]; - } - - void print(Sequence query, Sequence subject) const; - size_t top_node() const; - - vector nodes; - vector edges; -}; - -void smith_waterman(Sequence q, Sequence s, const Diag_graph &diags); \ No newline at end of file +} \ No newline at end of file diff --git a/src/chaining/diag_graph.h b/src/chaining/diag_graph.h new file mode 100644 index 000000000..ab4e4e78f --- /dev/null +++ b/src/chaining/diag_graph.h @@ -0,0 +1,175 @@ +#pragma once +#include "../util/geo/diagonal_segment.h" + +struct DiagonalNode : public DiagonalSegment +{ + enum { estimate, finished }; + DiagonalNode() : + DiagonalSegment(), + link_idx(-1), + prefix_score(0), + path_max(0), + path_min(0) + {} + DiagonalNode(int query_pos, int subject_pos, int len, int score, int link_idx = -1) : + DiagonalSegment(query_pos, subject_pos, len, score), + link_idx(link_idx), + prefix_score(score), + path_max(score), + path_min(score) + {} + DiagonalNode(const DiagonalSegment& d) : + DiagonalSegment(d), + link_idx(-1), + prefix_score(d.score), + path_max(d.score), + path_min(d.score) + {} + void deactivate() + { + link_idx = 0; + } + void reset() + { + link_idx = -1; + prefix_score = score; + path_max = score; + path_min = score; + } + bool is_maximum() const + { + return path_max == prefix_score; + } + int rel_score() const + { + return prefix_score == path_max ? prefix_score : prefix_score - path_min; + } + static bool cmp_prefix_score(const DiagonalNode* x, const DiagonalNode* y) + { + return x->prefix_score > y->prefix_score; + } + static bool cmp_rel_score(const DiagonalNode* x, const DiagonalNode* y) + { + return x->rel_score() > y->rel_score(); + } + int link_idx, prefix_score, path_max, path_min; +}; + +struct DiagGraph +{ + + enum { end = size_t(-1) }; + + struct Edge + { + Edge() : + prefix_score(0), + node_in() + { + } + Edge(int prefix_score, int path_max, int j, unsigned node_in, unsigned node_out, int path_min, int prefix_score_begin) : + prefix_score(prefix_score), + path_max(path_max), + j(j), + path_min(path_min), + prefix_score_begin(prefix_score_begin), + node_in(node_in), + node_out(node_out) + { + } + /*operator int() const + { + return prefix_score; + } + bool operator<(const Edge &x) const + { + return prefix_score > x.prefix_score; + }*/ + int prefix_score, path_max, j, path_min, prefix_score_begin; + unsigned node_in, node_out; + }; + + void init() + { + nodes.clear(); + edges.clear(); + } + + void init(unsigned node) + { + if (edges.size() >= (size_t)std::numeric_limits::max()) + throw std::runtime_error("Too many edges."); + nodes[node].link_idx = (int)edges.size(); + } + + void load(std::vector::const_iterator begin, std::vector::const_iterator end); + void sort(); + void prune(); + void clear_edges(); + + std::vector::iterator add_edge(const Edge& edge) + { + for (std::vector::iterator j = nodes.begin() + edge.node_in + 1; j < nodes.end(); ++j) + if (j->link_idx == -1) + break; + else + ++j->link_idx; + assert(nodes[edge.node_in].link_idx >= 0 && nodes[edge.node_in].link_idx <= (int)edges.size()); + DiagonalNode& d = nodes[edge.node_in]; + if (edge.prefix_score > d.prefix_score) { + d.prefix_score = edge.prefix_score; + d.path_max = edge.path_max; + d.path_min = edge.path_min; + } + return edges.insert(edges.begin() + d.link_idx++, edge); + } + + std::vector::const_iterator get_edge(size_t node, int j) const + { + const DiagonalNode& d = nodes[node]; + if (d.score == 0) + return edges.begin() + d.link_idx - 1; + if (edges.empty()) + return edges.end(); + int max_score = d.score; + ptrdiff_t max_i = -1; + for (ptrdiff_t i = d.link_idx - 1; i >= 0 && edges[i].node_in == node; --i) + if (edges[i].j < j && edges[i].prefix_score > max_score) { + max_i = i; + max_score = edges[i].prefix_score; + } + + /*for (vector::const_iterator i = edges.begin() + d.link_idx - 1; i >= edges.begin() && i->node_in == node; --i) + if (i->j < j && i->prefix_score > max_score) { + max_edge = i; + max_score = i->prefix_score; + }*/ + return max_i >= 0 ? edges.begin() + max_i : edges.end(); + } + + int prefix_score(size_t node, int j, int& path_max, int& path_min) const + { + const std::vector::const_iterator i = get_edge(node, j); + path_max = i == edges.end() ? nodes[node].score : std::max(nodes[node].score, i->path_max); + path_min = i == edges.end() ? nodes[node].score : i->path_min; + return i == edges.end() ? nodes[node].score : std::max(nodes[node].score, i->prefix_score); + } + + DiagonalNode& operator[](size_t k) + { + return nodes[k]; + } + + const DiagonalNode& operator[](size_t k) const + { + return nodes[k]; + } + + void print(Sequence query, Sequence subject) const; + size_t top_node() const; + + std::vector nodes; + std::vector edges; +}; + +void smith_waterman(Sequence q, Sequence s, const DiagGraph& diags); \ No newline at end of file diff --git a/src/chaining/greedy_align.cpp b/src/chaining/greedy_align.cpp index a515cb4fe..de13d61bf 100644 --- a/src/chaining/greedy_align.cpp +++ b/src/chaining/greedy_align.cpp @@ -8,7 +8,7 @@ Code developed by Benjamin Buchfink This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. +(at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -29,9 +29,12 @@ along with this program. If not, see . #include "../stats/score_matrix.h" //#include "../align/extend_ungapped.h" #include "../output/output_format.h" -#include "../dp/hsp_traits.h" +#include "../util/hsp/approx_hsp.h" #include "chaining.h" #include "../util/util.h" +#include "../dp/ungapped.h" +#include "aligner.h" +#include "diag_graph.h" using std::endl; using std::cout; @@ -40,52 +43,21 @@ using std::list; using std::set; using std::max; using std::min; +using std::vector; -bool disjoint(list::const_iterator begin, list::const_iterator end, const Hsp_traits &t, int cutoff) -{ - for (; begin != end; ++begin) { - const double ot = t.subject_range.overlap_factor(begin->subject_range), - oq = t.query_range.overlap_factor(begin->query_range); - if ((1.0 - min(ot, oq)) * t.score / begin->score >= config.chaining_stacked_hsp_ratio) - continue; - if ((1.0 - max(ot, oq)) * t.score < cutoff) - return false; - //if (begin->partial_score(t) < cutoff || !begin->collinear(t)) - //if (!begin->disjoint(t) || !begin->collinear(t)) - //if (!begin->rel_disjoint(t)) - // return false; - } - return true; -} - -bool disjoint(list::const_iterator begin, list::const_iterator end, const Diagonal_segment &d, int cutoff) -{ - for (; begin != end; ++begin) { - const double ot = d.subject_range().overlap_factor(begin->subject_range), - oq = d.query_range().overlap_factor(begin->query_range); - if ((1.0 - min(ot, oq)) * d.score / begin->score >= config.chaining_stacked_hsp_ratio) - continue; - if ((1.0 - max(ot, oq)) * d.score < cutoff) - return false; - //if (begin->partial_score(d) < cutoff || !begin->collinear(d)) - //if (!begin->disjoint(d) || !begin->collinear(d)) - //if (!begin->rel_disjoint(d)) - //return false; - } - return true; -} +static const double SPACE_PENALTY = 0.1; -void Diag_graph::clear_edges() +void DiagGraph::clear_edges() { edges.clear(); - for (vector::iterator i = nodes.begin(); i < nodes.end(); ++i) + for (vector::iterator i = nodes.begin(); i < nodes.end(); ++i) i->deactivate(); } -void Diag_graph::load(vector::const_iterator begin, vector::const_iterator end) +void DiagGraph::load(vector::const_iterator begin, vector::const_iterator end) { int d = std::numeric_limits::min(), max_j_end = d; - for (vector::const_iterator i = begin; i < end; ++i) { + for (vector::const_iterator i = begin; i < end; ++i) { const int d2 = i->diag(); if (d2 != d) { d = d2; @@ -99,17 +71,17 @@ void Diag_graph::load(vector::const_iterator begin, vectortranscript.push_back(op_match); - ++out->identities; - } - else - out->transcript.push_back(op_substitution, s); - ++out->length; - } - } - return true; - } - - void backtrace(size_t top_node, Hsp *out, Hsp_traits &t, int max_shift, unsigned &next, int max_j) const - { - Hsp_traits traits(frame); - if (top_node != Diag_graph::end) { - const Diagonal_node &d = diags[top_node]; - if (out) { - out->transcript.clear(); - out->query_range.end_ = d.query_end(); - out->subject_range.end_ = d.subject_end(); - } - traits.subject_range.end_ = d.subject_end(); - traits.query_range.end_ = d.query_end(); - int score_min = d.prefix_score; - backtrace(top_node, std::min(d.subject_end(), max_j), out, traits, d.prefix_score, score_min, max_shift, next); - } - else { - traits.score = 0; - if (out) - out->score = 0; - } - if (out) - out->transcript.push_terminator(); - t = traits; - } - - int backtrace(size_t top_node, list &hsps, list &ts, list::iterator &t_begin, int cutoff, int max_shift) const - { - unsigned next; - int max_score = 0, max_j = (int)subject.length(); - do { - Hsp *hsp = log ? new Hsp(true) : 0; - Hsp_traits t(frame); - next = std::numeric_limits::max(); - backtrace(top_node, hsp, t, max_shift, next, max_j); - if (t.score > 0) - max_j = t.subject_range.begin_; - if (t.score >= cutoff && disjoint(t_begin, ts.end(), t, cutoff)) { - if (t_begin == ts.end()) { - ts.push_back(t); - t_begin = ts.end(); - t_begin--; - } - else - ts.push_back(t); - if (hsp) - hsps.push_back(*hsp); - max_score = std::max(max_score, t.score); - } - delete hsp; - top_node = next; - } while (next != std::numeric_limits::max()); - return max_score; - } - - int backtrace(list &hsps, list &ts, int cutoff, int max_shift) const - { - vector top_nodes; - for (size_t i = 0; i < diags.nodes.size(); ++i) { - Diagonal_node &d = diags.nodes[i]; - //cout << "node=" << i << " prefix_score=" << d.prefix_score << " path_max=" << d.path_max << " rel_score=" << d.rel_score() << " cutoff=" << cutoff << endl; - //if (d.prefix_score >= cutoff && (d.prefix_score == d.path_max || d.prefix_score - d.path_min >= cutoff)) - if(d.rel_score() >= cutoff) - top_nodes.push_back(&d); - } - std::sort(top_nodes.begin(), top_nodes.end(), Diagonal_node::cmp_rel_score); - int max_score = 0; - list::iterator t_begin = ts.end(); - - for (vector::const_iterator i = top_nodes.begin(); i < top_nodes.end(); ++i) { - const size_t node = *i - diags.nodes.data(); - if (log) - cout << "Backtrace candidate node=" << node << endl; - if (disjoint(t_begin, ts.end(), **i, cutoff)) { - if (log) - cout << "Backtrace node=" << node << " prefix_score=" << (*i)->prefix_score << " rel_score=" << (*i)->rel_score() << endl; - max_score = std::max(max_score, backtrace(node, hsps, ts, t_begin, cutoff, max_shift)); - if (log) - cout << endl; - } - } - return max_score; - } - - int run(list &hsps, list &ts, double space_penalty, int cutoff, int max_shift) + int Aligner::run(list &hsps, list &ts, double space_penalty, int cutoff, int max_shift) { if (config.chaining_maxnodes > 0) { - std::sort(diags.nodes.begin(), diags.nodes.end(), Diagonal_segment::cmp_score); + std::sort(diags.nodes.begin(), diags.nodes.end(), DiagonalSegment::cmp_score); if (diags.nodes.size() > config.chaining_maxnodes) diags.nodes.erase(diags.nodes.begin() + config.chaining_maxnodes, diags.nodes.end()); } if (config.chaining_len_cap > 0.0 && diags.nodes.size() > config.chaining_min_nodes) { - std::sort(diags.nodes.begin(), diags.nodes.end(), Diagonal_segment::cmp_score); + std::sort(diags.nodes.begin(), diags.nodes.end(), DiagonalSegment::cmp_score); const double cap = query.length() * config.chaining_len_cap; double total_len = 0.0; auto it = diags.nodes.begin(); @@ -590,7 +385,7 @@ struct Greedy_aligner2 cout << endl << endl; } - forward_pass(Index_iterator(0llu), Index_iterator(diags.nodes.size()), true, space_penalty); + forward_pass(IndexIterator(0llu), IndexIterator(diags.nodes.size()), true, space_penalty); int max_score = backtrace(hsps, ts, cutoff, max_shift); if (log) { @@ -604,16 +399,16 @@ struct Greedy_aligner2 return max_score; } - int run(list &hsps, list &ts, vector::const_iterator begin, vector::const_iterator end, int band) + int Aligner::run(list &hsps, list &ts, vector::const_iterator begin, vector::const_iterator end, int band) { if (log) cout << "***** Seed hit run " << begin->diag() << '\t' << (end - 1)->diag() << '\t' << (end - 1)->diag() - begin->diag() << endl; diags.init(); diags.load(begin, end); - return run(hsps, ts, 0.1, 19, band); + return run(hsps, ts, SPACE_PENALTY, 19, band); } - Greedy_aligner2(const Sequence &query, const Sequence &subject, bool log, unsigned frame) : + Aligner::Aligner(const Sequence &query, const Sequence &subject, bool log, unsigned frame) : query(query), subject(subject), //query_bc(query_bc), @@ -622,26 +417,89 @@ struct Greedy_aligner2 { } - const Sequence query, subject; - //const Bias_correction &query_bc; - const bool log; - const unsigned frame; - static thread_local Diag_graph diags; - static thread_local map window; +thread_local DiagGraph Aligner::diags; +thread_local map Aligner::window; -}; +} -thread_local Diag_graph Greedy_aligner2::diags; -thread_local map Greedy_aligner2::window; +namespace Chaining { -std::pair> greedy_align(Sequence query, Sequence subject, vector::const_iterator begin, vector::const_iterator end, bool log, unsigned frame) +static Score merge_score(const ApproxHsp& h1, const ApproxHsp& h2) { + static const double GAP_PENALTY = 0.5; + const Loc gq = h2.query_range.begin_ - h1.query_range.end_, gt = h2.subject_range.begin_ - h1.subject_range.end_; + if (gq < 0 || gt < 0) + return 0; + const Score s = h1.score + h2.score; + if (gq > gt) { + return Score(s - gq * GAP_PENALTY - gt * SPACE_PENALTY); + } + else + return Score(s - gt * GAP_PENALTY - gq * SPACE_PENALTY); +} + +static ApproxHsp merge(const ApproxHsp& h1, const ApproxHsp& h2) { + ApproxHsp h(h1.frame); + h.d_max = max(h1.d_max, h2.d_max); + h.d_min = min(h1.d_min, h2.d_min); + h.query_range = { h1.query_range.begin_, h2.query_range.end_ }; + h.query_source_range = h.query_range; + h.subject_range = { h1.subject_range.begin_, h2.subject_range.end_ }; + h.score = merge_score(h1, h2); + h.evalue = 0; + if (h1.max_diag.score > h2.max_diag.score) { + h.max_diag = h1.max_diag; + h.max_diag.d_max_right = max(h.max_diag.d_max_right, h2.d_max); + h.max_diag.d_min_right = min(h.max_diag.d_min_right, h2.d_min); + } + else { + h.max_diag = h2.max_diag; + h.max_diag.d_max_left = max(h.max_diag.d_max_left, h1.d_max); + h.max_diag.d_min_left = min(h.max_diag.d_min_left, h1.d_min); + } + return h; +} + +static void merge_hsps(list& hsps) { + auto it = hsps.begin(); + while (it != hsps.end()) { + auto it2 = it; + ++it2; + while (it2 != hsps.end()) { + if (merge_score(*it, *it2) > max(it->score, it2->score)) { + *it = merge(*it, *it2); + it2 = hsps.erase(it2); + } + else if (merge_score(*it2, *it) > max(it->score, it2->score)) { + *it = merge(*it2, *it); + it2 = hsps.erase(it2); + } + else + ++it2; + } + ++it; + } +} + +std::pair> run(Sequence query, Sequence subject, vector::const_iterator begin, vector::const_iterator end, bool log, unsigned frame) { const int band = config.chaining_maxgap; - if (end - begin == 1) - return { begin->score, { { begin->diag(), begin->diag(), begin->score, (int)frame, begin->query_range(), begin->subject_range() } } }; - Greedy_aligner2 ga(query, subject, log, frame); + if (end - begin == 1) { + const Loc d = begin->diag(); + const Anchor anchor(*begin, d, d, d, d, begin->score); + return { begin->score, { { d, d, begin->score, (int)frame, begin->query_range(), begin->subject_range(), anchor}} }; + } + Chaining::Aligner ga(query, subject, log, frame); list hsps; - list ts; + list ts; int score = ga.run(hsps, ts, begin, end, band); + if (!config.no_chaining_merge_hsps) + merge_hsps(ts); return std::make_pair(score, std::move(ts)); +} + +list run(Sequence query, const std::vector& targets) { + list out; + return out; +} + } \ No newline at end of file diff --git a/src/chaining/hamming_ext.cpp b/src/chaining/hamming_ext.cpp new file mode 100644 index 000000000..f30e0f856 --- /dev/null +++ b/src/chaining/hamming_ext.cpp @@ -0,0 +1,53 @@ +#include "chaining.h" +#include "../basic/config.h" +#include "../stats/score_matrix.h" +#include "../stats/stats.h" +#include "../util/util.h" + +using std::stable_sort; +using std::vector; +using std::max; + +namespace Chaining { + +static ApproxHsp find_aln(vector::iterator begin, vector::iterator end, Loc qlen, Loc tlen) { + for (auto it = begin; it < end; ++it) { + const double ev = score_matrix.evalue(it->score, qlen, tlen); + if ((it->id_percent() >= config.approx_min_id || Stats::approx_id(it->score, it->len, it->len) >= config.approx_min_id) + && max(it->cov_percent(qlen), it->cov_percent(tlen)) >= config.member_cover + && ev <= config.max_evalue) + return ApproxHsp(0, 0, it->score, 0, it->query_range(), it->subject_range(), *it, ev); + } + return ApproxHsp(0); +} + +static ApproxHsp filter(vector::iterator begin, vector::iterator end, Loc qlen, Loc tlen) { + const double TOLERANCE_FACTOR = 1.1; + stable_sort(begin, end, DiagonalSegment::cmp_score); + Loc ident = 0, len = 0; + const Loc qtol = safe_cast(qlen * TOLERANCE_FACTOR), ttol = safe_cast(tlen * TOLERANCE_FACTOR); + for (auto it = begin; it < end; ++it) { + if (len + it->len > qtol || len + it->len > ttol) + continue; + ident += it->ident; + len += it->len; + } + if (max((double)len / qlen, (double)len / tlen) * 100.0 < config.diag_filter_cov + || (double)ident / len * 100.0 < config.diag_filter_id) + return ApproxHsp(0, -1); + return ApproxHsp(0); +} + +ApproxHsp hamming_ext(vector::iterator begin, vector::iterator end, Loc qlen, Loc tlen) { + if (config.hamming_ext) { + ApproxHsp h = find_aln(begin, end, qlen, tlen); + if (h.score > 0) + return h; + } + if (config.diag_filter_cov > 0.0 || config.diag_filter_id > 0.0) { + return filter(begin, end, qlen, tlen); + } + return ApproxHsp(0); +} + +} \ No newline at end of file diff --git a/src/chaining/smith_waterman.cpp b/src/chaining/smith_waterman.cpp index 5a664689d..27cf4648d 100644 --- a/src/chaining/smith_waterman.cpp +++ b/src/chaining/smith_waterman.cpp @@ -3,16 +3,19 @@ #include "../output/output_format.h" #include "../dp/dp.h" #include "../dp/ungapped.h" +#include "diag_graph.h" +#include "../dp/scalar/scalar.h" using std::cout; using std::endl; +using std::vector; -void print_diag(int i0, int j0, int l, int score, const Diag_graph &diags, const Sequence &query, const Sequence &subject) +void print_diag(int i0, int j0, int l, int score, const DiagGraph &diags, const Sequence &query, const Sequence &subject) { - Diagonal_segment ds(i0, j0, l, 0); + DiagonalSegment ds(i0, j0, l, 0); unsigned n = 0; int path_max, path_min; - for (vector::const_iterator d = diags.nodes.begin(); d != diags.nodes.end(); ++d) { + for (vector::const_iterator d = diags.nodes.begin(); d != diags.nodes.end(); ++d) { if (d->intersect(ds).len > 0) { if (d->score == 0) continue; @@ -32,10 +35,10 @@ void print_diag(int i0, int j0, int l, int score, const Diag_graph &diags, const cout << "Diag n=x i=" << i0 << " j=" << j0 << " len=" << l << " prefix_score=" << score << endl; } -void smith_waterman(Sequence q, Sequence s, const Diag_graph &diags) +void smith_waterman(Sequence q, Sequence s, const DiagGraph &diags) { Hsp hsp(true); - //smith_waterman(q, s, hsp); + smith_waterman(q, s, hsp); Hsp::Iterator i = hsp.begin(); int i0 = -1, j0 = -1, l = 0, score = 0; for (; i.good(); ++i) { diff --git a/src/cluster/cascaded/cascaded.cpp b/src/cluster/cascaded/cascaded.cpp new file mode 100644 index 000000000..481ef7b89 --- /dev/null +++ b/src/cluster/cascaded/cascaded.cpp @@ -0,0 +1,126 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2013-2018 Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#include +#include "cascaded.h" +#include "../util/util.h" +#include "../util/sequence/sequence.h" +#include "../output/output_format.h" +#include "../util/algo/algo.h" +#include "../../basic/statistics.h" +#include "../../run/workflow.h" + +const char* const DEFAULT_MEMORY_LIMIT = "16G"; + +using std::vector; +using std::shared_ptr; +using std::endl; +using std::move; +using std::runtime_error; +using std::numeric_limits; +using std::to_string; +using std::tie; +using std::string; +using std::pair; +using std::iota; + +namespace Cluster { + +string Cascaded::get_description() { + return "Cascaded greedy vertex cover algorithm"; +} + +static BitVector rep_bitset(const vector ¢roid, const BitVector *superset = nullptr) { + BitVector r(centroid.size()); + for (SuperBlockId c : centroid) + if (!superset || superset->get(c)) + r.set(c); + return r; +} + +vector cluster(shared_ptr& db, const shared_ptr& filter, const SuperBlockId* member_counts, bool last_round) { + using Edge = Util::Algo::Edge; + statistics.reset(); + config.command = Config::blastp; + config.output_format = { "edge" }; + config.query_cover = 0; + config.subject_cover = 0; + config.query_or_target_cover = config.member_cover; + config.algo = Config::Algo::DOUBLE_INDEXED; + config.max_target_seqs_ = INT64_MAX; + config.self = true; + config.iterate.unset(); + config.mapany = false; + tie(config.chunk_size, config.lowmem_) = block_size(Util::String::interpret_number(config.memory_limit.get(DEFAULT_MEMORY_LIMIT)), config.sensitivity, config.lin_stage1); + + shared_ptr callback(new Callback); + + Search::run(db, nullptr, callback, filter); + + message_stream << "Finished search. #Edges: " << callback->count << endl; + task_timer timer("Allocating buffers"); + vector edges(callback->count); + timer.go("Loading edges"); + InputFile f(callback->edge_file); + f.read(edges.data(), callback->count); + f.close_and_delete(); + timer.go("Sorting edges"); + db->reopen(); + FlatArray edge_array = make_flat_array_dense(move(edges), (SuperBlockId)db->sequence_count(), config.threads_, Edge::GetKey()); + timer.finish(); + + const auto algo = from_string(config.graph_algo); + return algo == GraphAlgo::GREEDY_VERTEX_COVER ? + Util::Algo::greedy_vertex_cover(edge_array, config.weighted_gvc ? member_counts : nullptr, last_round && !config.strict_gvc) + : len_sorted_clust(edge_array); +} + +static pair, BitVector> update_clustering(const BitVector& previous_filter, const vector& previous_centroids, vector&& current_centroids, int round) { + BitVector oid_filter(round == 0 ? rep_bitset(current_centroids) : rep_bitset(current_centroids, &previous_filter)); + if (round > 0) + for (size_t i = 0; i < current_centroids.size(); ++i) + if (!previous_filter.get(i)) + current_centroids[i] = current_centroids[previous_centroids[i]]; + return { current_centroids, oid_filter }; +} + +vector cascaded(shared_ptr& db) { + if (db->sequence_count() > (int64_t)numeric_limits::max()) + throw runtime_error("Workflow supports a maximum of " + to_string(numeric_limits::max()) + " input sequences."); + const auto steps = cluster_steps(config.approx_min_id); + shared_ptr oid_filter(new BitVector); + int64_t cluster_count = db->sequence_count(); + vector centroids(cluster_count); + iota(centroids.begin(), centroids.end(), 0); + + for (int i = 0; i < (int)steps.size(); i++) { + task_timer timer; + config.lin_stage1 = ends_with(steps[i], "_lin"); + config.sensitivity = from_string(rstrip(steps[i], "_lin")); + tie(centroids, *oid_filter) = update_clustering(*oid_filter, + centroids, + cluster(db, i == 0 ? nullptr : oid_filter, config.weighted_gvc ? member_counts(centroids).data() : nullptr, i == (int)steps.size() - 1), + i); + const int64_t n = oid_filter->one_count(); + message_stream << "Clustering round " << i + 1 << " complete. #Input sequences: " << cluster_count << " #Clusters: " << n << " Time: " << timer.seconds() << 's' << endl; + cluster_count = n; + } + return centroids; +} + +} diff --git a/src/cluster/cascaded/cascaded.h b/src/cluster/cascaded/cascaded.h new file mode 100644 index 000000000..cbf23766d --- /dev/null +++ b/src/cluster/cascaded/cascaded.h @@ -0,0 +1,60 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2013-2018 Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once +#include "../cluster.h" + +namespace Cluster { + +struct Cascaded : public ClusteringAlgorithm { + ~Cascaded(){}; + void run(); + std::string get_description(); + static std::string get_key(){ + return "cascaded"; + } +}; + +std::vector cascaded(std::shared_ptr& db); +std::vector cluster_steps(double approx_id); + +struct Callback : public Consumer { + using Edge = Util::Algo::Edge; + Callback() : + count(0) + {} + virtual void consume(const char* ptr, size_t n) override { + const char* end = ptr + n; + while (ptr < end) { + const auto edge = *(Output::Format::Edge::Data*)ptr; + ptr += sizeof(Output::Format::Edge::Data); + if (edge.qcovhsp >= config.member_cover) { + edge_file.write(Edge((SuperBlockId)edge.target, (SuperBlockId)edge.query, edge.evalue)); + ++count; + } + if (edge.scovhsp >= config.member_cover) { + edge_file.write(Edge((SuperBlockId)edge.query, (SuperBlockId)edge.target, edge.evalue)); + ++count; + } + } + } + TempFile edge_file; + int64_t count; +}; + +} diff --git a/src/cluster/cascaded/helpers.cpp b/src/cluster/cascaded/helpers.cpp new file mode 100644 index 000000000..46b68bc56 --- /dev/null +++ b/src/cluster/cascaded/helpers.cpp @@ -0,0 +1,19 @@ +#include "cascaded.h" + +using std::string; +using std::vector; + +namespace Cluster { + +vector cluster_steps(double approx_id) { + if (!config.cluster_steps.empty()) + return config.cluster_steps; + vector v = { "faster_lin", "fast" }; + if (approx_id < 90) + v.push_back("default"); + if (approx_id < 50) + v.push_back("more-sensitive"); + return v; +} + +} \ No newline at end of file diff --git a/src/cluster/cascaded/recluster.cpp b/src/cluster/cascaded/recluster.cpp new file mode 100644 index 000000000..1d1fd6124 --- /dev/null +++ b/src/cluster/cascaded/recluster.cpp @@ -0,0 +1,201 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#include "cascaded.h" +#include "../../basic/config.h" +#include "../../data/fasta/fasta_file.h" +#include "../../run/workflow.h" +#include "../../output/output_format.h" +#include "../../basic/statistics.h" +#include "../search/search.h" + +using std::unique_ptr; +using std::endl; +using std::vector; +using std::function; +using std::make_shared; +using std::shared_ptr; +using std::for_each; +using std::to_string; +using std::string; +using std::tie; +using std::pair; + +namespace Cluster { + +static vector recluster(shared_ptr& db, const vector& clustering, int iteration) { + task_timer timer(("*** Initializing recluster iteration " + to_string(iteration + 1)).c_str()); + + FlatArray clusters; + vector centroids; + tie(clusters, centroids) = cluster_sorted(clustering); + + BitVector centroid_aligned(db->sequence_count()); + for_each(centroids.begin(), centroids.end(), [¢roid_aligned](OId c) { centroid_aligned.set(c); }); + function callback([¢roid_aligned](const HspContext& h) { + if (h.scovhsp() >= config.member_cover && (h.approx_id() >= config.approx_min_id || h.id_percent() >= config.approx_min_id)) + centroid_aligned.set(h.subject_oid); + }); + timer.finish(); + + HspValues hsp_values = HspValues::TARGET_COORDS; + if (config.approx_min_id > 0) + hsp_values |= HspValues::QUERY_COORDS | HspValues::IDENT | HspValues::LENGTH; + realign(clusters, centroids, *db, callback, hsp_values); + + timer.go("Creating database of unaligned sequences"); + const vector unal_members = centroid_aligned.negative_list(); + if (unal_members.empty()) + return clustering; + shared_ptr unaligned(db->sub_db(unal_members.cbegin(), unal_members.cend())); + unaligned->set_seqinfo_ptr(0); + timer.finish(); + message_stream << "#Sequences that failed to align against assigned centroid: " << unal_members.size() << endl; + + timer.go("Creating centroid database"); + shared_ptr centroid_db(db->sub_db(centroids.cbegin(), centroids.cend())); + centroid_db->set_seqinfo_ptr(0); + timer.finish(); + + statistics.reset(); + config.command = Config::blastp; + config.max_target_seqs_ = 1; + config.iterate = vector(); + config.output_format = { "edge" }; + config.self = false; + config.query_cover = config.no_recluster_bd ? config.member_cover : 0; + config.subject_cover = 0; + config.query_or_target_cover = config.no_recluster_bd ? 0 : config.member_cover; + config.sensitivity = from_string(cluster_steps(config.approx_min_id).back()); + //tie(config.chunk_size, config.lowmem_) = block_size(Util::String::interpret_number(config.memory_limit.get(DEFAULT_MEMORY_LIMIT)), Search::iterated_sens.at(config.sensitivity).front(), false); + config.lowmem_ = 1; + config.chunk_size = 4.0; + shared_ptr mapback = make_shared(unal_members.size()); + Search::run(centroid_db, unaligned, mapback); + + timer.go("Updating clustering"); + vector out = clustering; + update_clustering(out.begin(), mapback->centroid_id.cbegin(), unal_members.cbegin(), unal_members.cend(), centroids.cbegin()); + + timer.go("Deallocating memory"); + centroid_db.reset(); + timer.finish(); + + vector unmapped_members = mapback->unmapped(); + message_stream << "#Sequences that failed to align against any centroid: " << unmapped_members.size() << endl; + if (unmapped_members.empty()) + return out; + + shared_ptr unmapped; + if (config.no_recluster_bd) { + timer.go("Creating database of unmapped sequences"); + unmapped.reset(unaligned->sub_db(unmapped_members.cbegin(), unmapped_members.cend())); + } + else { + timer.go("Loading covered centroids list"); + vector> covered_centroids = mapback->targets_covered(); + timer.finish(); + message_stream << "#Centroid sequences tentatively covered: " << covered_centroids.size() << endl; + timer.go("Filtering list"); + ips4o::parallel::sort(covered_centroids.begin(), covered_centroids.end()); + vector centroid_list; + auto it = covered_centroids.begin(); + auto it2 = unmapped_members.begin(); + while (it < covered_centroids.end() && it2 < unmapped_members.end()) { + if (it->first < *it2) + ++it; + else if (*it2 < it->first) + ++it2; + else { + centroid_list.push_back(it->second); + ++it; + } + } + ips4o::parallel::sort(centroid_list.begin(), centroid_list.end()); + auto end = std::unique(centroid_list.begin(), centroid_list.end()); + const int64_t n = end - centroid_list.begin(); + timer.finish(); + message_stream << "#Centroid sequences covered: " << n << endl; + timer.go("Making sequence list for reclustering"); + for (int64_t i = 0; i < unmapped_members.size(); ++i) + unmapped_members[i] = unal_members[unmapped_members[i]]; + const vector members = cluster_members(centroid_list.begin(), end, clusters); + unmapped_members.reserve(unmapped_members.size() + members.size()); + unmapped_members.insert(unmapped_members.end(), members.begin(), members.end()); + //unmapped_members.reserve(unmapped_members.size() + n); + //for (auto i = centroid_list.begin(); i < end; ++i) + //unmapped_members.push_back(centroids[*i]); + ips4o::parallel::sort(unmapped_members.begin(), unmapped_members.end()); + timer.finish(); + message_stream << "#Sequences from covered clustes: " << members.size() << endl; + timer.go("Creating database for reclustering"); + unmapped.reset(db->sub_db(unmapped_members.cbegin(), unmapped_members.cend())); + } + mapback.reset(); + timer.finish(); + + timer.go("Deallocating memory"); + clusters.clear(); + clusters.shrink_to_fit(); + unaligned.reset(); + timer.finish(); + + const vector reclust = recluster(unmapped, convert_mapping(cascaded(unmapped), OId()), iteration + 1); + + timer.go("Deallocating memory"); + unmapped.reset(); + + timer.go("Merging clusterings"); + for (SuperBlockId i = 0; i < (SuperBlockId)reclust.size(); ++i) + out[unal_members[unmapped_members[i]]] = unal_members[unmapped_members[reclust[i]]]; + + return out; +} + +void recluster() { + config.database.require(); + config.clustering.require(); + init_thresholds(); + //config.strict_gvc = true; + message_stream << "Coverage cutoff: " << config.member_cover << '%' << endl; + + task_timer timer("Opening the database"); + shared_ptr db(SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NEED_LETTER_COUNT | SequenceFile::Flags::ACC_TO_OID_MAPPING | SequenceFile::Flags::OID_TO_ACC_MAPPING, SequenceFile::Metadata())); + config.db_size = db->letters(); + timer.finish(); + unique_ptr out(open_out_tsv()); + message_stream << "#Database sequences: " << db->sequence_count() << ", #Letters: " << db->letters() << endl; + + timer.go("Reading the input file"); + const vector clustering = read(config.clustering, *db); + timer.finish(); + + const vector member2centroid = recluster(db, clustering, 0); + + timer.go("Generating output"); + if (flag_any(db->format_flags(), SequenceFile::FormatFlags::TITLES_LAZY)) + db->init_random_access(0, 0, false); + output_mem(*out, *db, member2centroid); + + timer.go("Closing the database"); + db.reset(); +} + +} \ No newline at end of file diff --git a/src/cluster/cascaded/wrapper.cpp b/src/cluster/cascaded/wrapper.cpp new file mode 100644 index 000000000..07a95971b --- /dev/null +++ b/src/cluster/cascaded/wrapper.cpp @@ -0,0 +1,188 @@ +#include +#include "cascaded.h" +#include "../data/fasta/fasta_file.h" +#include "../run/workflow.h" +#include "../util/system/system.h" + +using std::shared_ptr; +using std::endl; +using std::tie; +using std::pair; +using std::iota; +using std::string; +using std::vector; +using std::ostream; +using std::ofstream; +using std::cout; +using std::numeric_limits; +using std::tuple; +using std::get; +using std::ignore; +using std::back_inserter; +using std::unique_ptr; +using namespace Util::Tsv; + +namespace Cluster { + +struct Config { + Config(shared_ptr& db) : + message_stream(true), + verbosity(1), + sens(from_string(rstrip(cluster_steps(config.approx_min_id).back(), "_lin"))), + output_format(init_output(-1)), + centroids(new FastaFile("", true, FastaFile::WriteAccess())), + seqs_processed(0), + letters_processed(0), + oid_to_centroid_oid(new File(Schema{ Type::INT64, Type::INT64 }, "", Flags::TEMP)) + { + } + MessageStream message_stream; + int verbosity; + Sensitivity sens; + double block_size; + std::unique_ptr output_format; + std::shared_ptr centroids; + task_timer total_time; + int64_t seqs_processed; + int64_t letters_processed; + std::vector oid2centroid; + std::vector centroid2oid; + std::unique_ptr oid_to_centroid_oid; +}; + +struct BestCentroid : public Consumer, public vector { + BestCentroid(OId block_size) : + vector(block_size, -1) + {} + virtual void consume(const char *ptr, size_t n) { + const char* const end = ptr + n; + while (ptr < end) { + const auto edge = *(Output::Format::Edge::Data*)ptr; + ptr += sizeof(Output::Format::Edge::Data); + this->operator[](edge.query) = edge.target; + } + } + virtual void finalize() {} + virtual ~BestCentroid() = default; +}; + +static vector search_vs_centroids(shared_ptr& super_block, const OId* super_block_id_to_oid, Config& cfg) { + //if (cfg.verbosity >= 2) + message_stream << "Searching vs. centroids #sequences = " << super_block->sequence_count() << " , #centroids = " << cfg.centroids->sequence_count() << endl; + + config.output_format = { "edge" }; + config.self = false; + config.max_target_seqs_ = 1; + config.toppercent = 100; + config.sensitivity = cfg.sens; + config.query_cover = config.member_cover; + config.subject_cover = 0; + config.query_or_target_cover = 0; + config.iterate = vector(); + tie(config.chunk_size, config.lowmem_) = block_size(Util::String::interpret_number(config.memory_limit.get(DEFAULT_MEMORY_LIMIT)), cfg.sens, false); + cfg.centroids->set_seqinfo_ptr(0); + shared_ptr best_centroid(new BestCentroid(super_block->sequence_count())); + log_rss(); + Search::run(cfg.centroids, super_block, best_centroid); + + int64_t clustered = 0; + vector unaligned; + for (SuperBlockId i = 0; i < super_block->sequence_count(); ++i) { + const OId oid = super_block_id_to_oid[i]; + if (best_centroid->operator[](i) == -1) { + unaligned.push_back(i); + } + else { + const OId centroid_oid = cfg.centroid2oid[best_centroid->operator[](i)]; + cfg.oid_to_centroid_oid->write_record(centroid_oid, oid); + ++clustered; + } + } + + //if (cfg.verbosity >= 2) + cfg.message_stream << clustered << " sequences assigned to clusters, " << unaligned.size() << " unaligned." << endl; + return unaligned; +} + +void Cascaded::run() { + config.database.require(); + init_thresholds(); + config.hamming_ext = config.approx_min_id >= 50.0; + task_timer total_time; + task_timer timer("Opening the input file"); + shared_ptr db(SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NEED_LETTER_COUNT | SequenceFile::Flags::OID_TO_ACC_MAPPING)); + if (db->type() == SequenceFile::Type::BLAST) + throw std::runtime_error("Clustering is not supported for BLAST databases."); + timer.finish(); + message_stream << "Input database: " << db->file_name() << " (" << db->sequence_count() << " sequences, " << db->letters() << " letters)" << endl; + const int64_t block_size = (int64_t)(::block_size(Util::String::interpret_number(config.memory_limit.get(DEFAULT_MEMORY_LIMIT)), Sensitivity::FASTER, true).first * 1e9); + unique_ptr out(open_out_tsv()); + + if (block_size >= (double)db->letters() && db->sequence_count() < numeric_limits::max()) { + const auto centroids = cascaded(db); + timer.go("Generating output"); + output_mem(*out, *db, centroids); + } + else { + timer.go("Length sorting the input file"); + Config cfg(db); + config.db_size = db->letters(); + vector, Util::Tsv::File*>> super_blocks = db->length_sort(block_size); + timer.finish(); + config.freq_masking = true; + int i = 0; + for (auto& b : super_blocks) { + message_stream << "Processing super block " << (i++) + 1 << '/' << super_blocks.size() << endl; + log_rss(); + log_stream << "Mem_sizes " << db->mem_size() << ' ' << cfg.centroids->mem_size() << endl; + vector super_block_id_to_oid; + Util::Tsv::File* oid_mapping; + tie(ignore, super_block_id_to_oid, oid_mapping) = b; + shared_ptr seqs(get<0>(b)); + shared_ptr unaligned_db; + vector unaligned; + timer.go("Reading super block mapping file"); + oid_mapping->template read(back_inserter(super_block_id_to_oid)); + timer.finish(); + log_rss(); + if (i == 1) { + unaligned_db = seqs; + unaligned.resize(seqs->sequence_count()); + iota(unaligned.begin(), unaligned.end(), 0); + } + else { + unaligned = search_vs_centroids(seqs, super_block_id_to_oid.data(), cfg); + timer.go("Creating subdatabase"); + unaligned_db.reset(seqs->sub_db(unaligned.cbegin(), unaligned.cend())); + timer.go("Freeing memory"); + seqs->close(); + seqs.reset(); + timer.finish(); + } + const vector clustering = cascaded(unaligned_db); + timer.go("Updating clustering"); + vector centroids; + for (SuperBlockId i = 0; i < (SuperBlockId)unaligned.size(); ++i) { + const OId member_oid = super_block_id_to_oid[unaligned[i]], centroid_oid = super_block_id_to_oid[unaligned[clustering[i]]]; + cfg.oid_to_centroid_oid->write_record(centroid_oid, member_oid); + if (member_oid == centroid_oid) { + cfg.centroid2oid.push_back(centroid_oid); + centroids.push_back(i); + } + } + unaligned_db->sub_db(centroids.cbegin(), centroids.cend(), cfg.centroids.get()); + timer.go("Freeing memory"); + unaligned_db->close(); + unaligned_db.reset(); + delete oid_mapping; + timer.finish(); + } + message_stream << "Total clusters: " << cfg.centroid2oid.size() << endl; + message_stream << "Total time: " << total_time.seconds() << 's' << endl; + timer.go("Generating output"); + output_mem(*out, *db, *cfg.oid_to_centroid_oid); + } + db->close(); +} + +} \ No newline at end of file diff --git a/src/cluster/cluster.h b/src/cluster/cluster.h index 740188f6d..984009720 100644 --- a/src/cluster/cluster.h +++ b/src/cluster/cluster.h @@ -19,9 +19,123 @@ along with this program. If not, see . #pragma once #include +#include +#include "../basic/value.h" +#include "../data/sequence_file.h" +#include "../util/data_structures/flat_array.h" +#include "../basic/match.h" +#include "../dp/flags.h" +#include "../output/output_format.h" +#include "../util/algo/algo.h" + class ClusteringAlgorithm { public: virtual void run() = 0; virtual std::string get_description() = 0; virtual ~ClusteringAlgorithm(){}; }; + +namespace Cluster { + +struct CentroidSorted {}; + +void realign(const FlatArray& clusters, const std::vector& centroids, SequenceFile& db, std::function& callback, HspValues hsp_values); +void realign(const std::vector& clustering, SequenceFile& db, std::function& callback, HspValues hsp_values); +template +std::pair, std::vector> read(const std::string& file_name, const SequenceFile& db, CentroidSorted); +template +std::vector read(const std::string& file_name, const SequenceFile& db); +template +std::vector member2centroid_mapping(const FlatArray& clusters, const std::vector& centroids); +template +void output_mem(Util::Tsv::File& out, SequenceFile& db, const std::vector& mapping); +void output_mem(Util::Tsv::File& out, SequenceFile& db, Util::Tsv::File& oid_to_centroid_oid); +template +void output_mem(Util::Tsv::File& out, SequenceFile& db, std::vector>& mapping); +template +std::pair, std::vector> cluster_sorted(const std::vector& mapping); +template +std::pair, std::vector> split(const std::vector& mapping); +std::vector member_counts(const std::vector& mapping); +Util::Tsv::File* open_out_tsv(); +void init_thresholds(); +std::vector len_sorted_clust(const FlatArray>& edges); + +template +std::vector convert_mapping(const std::vector& mapping, Int2) { + std::vector out; + out.reserve(mapping.size()); + std::transform(mapping.begin(), mapping.end(), std::back_inserter(out), [](Int x) { return (Int2)x; }); + return out; +} + +struct Mapback : public Consumer { + Mapback(int64_t count) : + centroid_id(count, -1), + count(0) + {} + virtual void consume(const char* ptr, size_t n) { + const char* end = ptr + n; + OId query = -1; + for(const char* p = ptr; p < end; p += sizeof(Output::Format::Edge::Data)) { + const auto edge = *(Output::Format::Edge::Data*)p; + assert(query == -1 || query == edge.query); + query = edge.query; + if (edge.qcovhsp >= config.member_cover) + centroid_id[edge.query] = edge.target; + } + if (query >= 0 && centroid_id[query] == -1) { + for (const char* p = ptr; p < end; p += sizeof(Output::Format::Edge::Data)) { + const auto edge = *(Output::Format::Edge::Data*)p; + if (edge.scovhsp >= config.member_cover) { + covered_centroids.write((OId)query); + covered_centroids.write((OId)edge.target); + ++count; + } + } + } + } + std::vector unmapped() const { + std::vector v; + for (OId i = 0; i < (OId)centroid_id.size(); ++i) + if (centroid_id[i] == -1) + v.push_back(i); + return v; + } + std::vector> targets_covered() { + std::vector> v; + v.resize(count); + InputFile f(covered_centroids); + f.read(v.data(), count); + f.close_and_delete(); + return v; + } + std::vector centroid_id; + TempFile covered_centroids; + int64_t count; +}; + +template +int64_t update_clustering(It clustering, It2 mapping, It2 query_begin, It2 query_end, It2 db_begin) { + const int64_t n = query_end - query_begin; + int64_t k = 0; + for (OId i = 0; i < n; ++i) + if (mapping[i] >= 0 && clustering[query_begin[i]] != db_begin[mapping[i]]) { + clustering[query_begin[i]] = (typename It::value_type)db_begin[mapping[i]]; + ++k; + } + return k; +} + +template +std::vector cluster_members(It begin, It end, const FlatArray& clusters) { + std::vector out; + for (It i = begin; i != end; ++i) { + for (auto it = clusters.cbegin(*i); it != clusters.cend(*i); ++it) { + out.push_back(*it); + } + } + return out; +} + +} \ No newline at end of file diff --git a/src/cluster/cluster_registry.h b/src/cluster/cluster_registry.h index a00b46bf0..43761a407 100644 --- a/src/cluster/cluster_registry.h +++ b/src/cluster/cluster_registry.h @@ -19,8 +19,11 @@ along with this program. If not, see . #pragma once #include "cluster.h" -#include "mcl.h" -#include "multi_step_cluster.h" +#ifdef WITH_MCL +#include "../contrib/mcl/mcl.h" +#endif +#include "cascaded/cascaded.h" +#include "incremental/incremental.h" namespace Workflow { namespace Cluster{ class ClusterRegistryStatic{ @@ -28,8 +31,11 @@ class ClusterRegistryStatic{ public: ClusterRegistryStatic(){ // To include new clustering algorithms add them into regMap - regMap[MultiStep::get_key()] = new MultiStep(); +#ifdef WITH_MCL regMap[MCL::get_key()] = new MCL(); +#endif + regMap[::Cluster::Cascaded::get_key()] = new ::Cluster::Cascaded(); + regMap[::Cluster::Incremental::Algo::get_key()] = new ::Cluster::Incremental::Algo(); } ~ClusterRegistryStatic(){ for(auto it = regMap.begin(); it != regMap.end(); it++){ @@ -37,18 +43,18 @@ class ClusterRegistryStatic{ it->second = nullptr; } } - ClusteringAlgorithm* get(string key) const{ + ClusteringAlgorithm* get(std::string key) const{ auto ca = regMap.find(key); if(ca == regMap.end()){ throw std::runtime_error("Clustering algorithm not found."); } return ca->second; } - bool has(string key) const{ + bool has(std::string key) const{ return regMap.find(key) != regMap.end(); } - vector getKeys() const{ - vector keys; + std::vector getKeys() const{ + std::vector keys; for(auto it = regMap.begin(); it != regMap.end(); it++){ keys.push_back(it->first); } @@ -61,13 +67,13 @@ class ClusterRegistry{ private: static const ClusterRegistryStatic reg; public: - static ClusteringAlgorithm* get(string key){ + static ClusteringAlgorithm* get(std::string key){ return reg.get(key); } - static bool has(string key){ + static bool has(std::string key){ return reg.has(key); } - static vector getKeys(){ + static std::vector getKeys(){ return reg.getKeys(); } }; diff --git a/src/cluster/helpers.cpp b/src/cluster/helpers.cpp new file mode 100644 index 000000000..0747eab79 --- /dev/null +++ b/src/cluster/helpers.cpp @@ -0,0 +1,220 @@ +#include +#include "cluster.h" +#include "../util/tsv/tsv.h" +#include "../util/string/tokenizer.h" +#include "../basic/config.h" +#include "../search/search.h" +#define _REENTRANT +#include "../lib/ips4o/ips4o.hpp" + +const char* const HEADER_LINE = "centroid\tmember"; + +using std::pair; +using std::endl; +using std::vector; +using std::ofstream; +using std::cout; +using std::ostream; +using std::runtime_error; +using std::string; +using std::for_each; +using std::floor; +using std::unique_ptr; +using std::back_inserter; +using std::less; +using namespace Util::Tsv; + +namespace Cluster { + +template +pair, vector> read(const string& file_name, const SequenceFile& db, CentroidSorted) { + const int64_t lines = Util::Tsv::count_lines(file_name); + TextInputFile in(file_name); + string centroid, member; + vector> pairs; + pairs.reserve(lines); + int64_t mappings = 0; + if (Blast_tab_format::header_format(::Config::cluster) == Header::SIMPLE) { + in.getline(); + if (in.line != HEADER_LINE) + throw runtime_error("Clusters file is missing header line."); + } + while (in.getline(), !in.eof() || !in.line.empty()) { + Util::String::Tokenizer(in.line, "\t") >> centroid >> member; + const Int centroid_oid = (Int)db.accession_to_oid(centroid).front(); + const Int member_oid = (Int)db.accession_to_oid(member).front(); + pairs.emplace_back(centroid_oid, member_oid); + ++mappings; + if (mappings % 1000000 == 0) + log_stream << "#Entries: " << mappings << endl; + } + in.close(); + return make_flat_array(pairs.begin(), pairs.end(), config.threads_); +} + +template pair, vector> read(const string&, const SequenceFile&, CentroidSorted); +template pair, vector> read(const string&, const SequenceFile&, CentroidSorted); + +template +vector read(const string& file_name, const SequenceFile& db) { + const int64_t lines = Util::Tsv::count_lines(file_name); + TextInputFile in(file_name); + string centroid, member; + vector v(db.sequence_count()); + int64_t mappings = 0; + if (Blast_tab_format::header_format(::Config::cluster) == Header::SIMPLE) { + in.getline(); + if (in.line != HEADER_LINE) + throw runtime_error("Clustering input file is missing header line."); + } + while (in.getline(), !in.eof() || !in.line.empty()) { + Util::String::Tokenizer(in.line, "\t") >> centroid >> member; + const auto centroid_oid = db.accession_to_oid(centroid); + const auto member_oid = db.accession_to_oid(member); + v[member_oid.front()] = (Int)centroid_oid.front(); + ++mappings; + if (mappings % 1000000 == 0) + log_stream << "#Entries: " << mappings << endl; + } + in.close(); + if (mappings != db.sequence_count()) + throw runtime_error("Invalid/incomplete clustering."); + return v; +} + +template vector read(const string&, const SequenceFile&); +template vector read(const string&, const SequenceFile&); + +template +vector member2centroid_mapping(const FlatArray& clusters, const vector& centroids) { + vector v(clusters.data_size()); + for (Int i = 0; i < (Int)centroids.size(); ++i) { + for (Int j : clusters[i]) + v[j] = centroids[i]; + } + return v; +} + +template +pair, vector> cluster_sorted(const vector& mapping) { + vector> pairs; + pairs.reserve(mapping.size()); + for (typename vector::const_iterator i = mapping.begin(); i < mapping.end(); ++i) + pairs.emplace_back(*i, Int(i - mapping.begin())); + return make_flat_array(pairs.begin(), pairs.end(), config.threads_); +} + +template pair, vector> cluster_sorted(const vector&); +template pair, vector> cluster_sorted(const vector&); + +void output(File& out, SequenceFile& db, File& oid_to_centroid_oid) { + unique_ptr sorted1(oid_to_centroid_oid.sort(1, config.threads_)); + unique_ptr joined1(join(*sorted1, db.seqid_file(), 1, 0, { {0,0},{1,1} })); + sorted1.reset(); + unique_ptr sorted2(joined1->sort(0, config.threads_)); + join(*sorted2, db.seqid_file(), 0, 0, { {1,1}, {0,1} }, out); +} + +template +void output_mem(File& out, SequenceFile& db, const FlatArray& clusters, const vector& centroids) { + const Util::Tsv::Table acc_mapping = db.seqid_file().read(config.threads_); + for (Int i = 0; i < (Int)centroids.size(); ++i) { + const string centroid = acc_mapping[centroids[i]].template get(0); + for(auto j = clusters.cbegin(i); j != clusters.cend(i); ++j) + out.write_record(centroid, acc_mapping[*j].template get(0)); + } +} + +template void output_mem(File&, SequenceFile&, const FlatArray&, const vector&); +template void output_mem(File&, SequenceFile&, const FlatArray&, const vector&); + +template +void output_mem(File& out, SequenceFile& db, const vector& mapping) { + vector centroids; + FlatArray clusters; + tie(clusters, centroids) = cluster_sorted(mapping); + output_mem(out, db, clusters, centroids); +} + +template void output_mem(File&, SequenceFile&, const vector&); +template void output_mem(File&, SequenceFile&, const vector&); + +template +void output_mem(File& out, SequenceFile& db, File& oid_to_centroid_oid) { + vector> centroid_oid; + oid_to_centroid_oid.template read(back_inserter(centroid_oid)); + vector centroids; + FlatArray clusters; + tie(clusters, centroids) = make_flat_array(centroid_oid.begin(), centroid_oid.end(), config.threads_); + output_mem(out, db, clusters, centroids); +} + +void output_mem(File& out, SequenceFile& db, File& oid_to_centroid_oid) { + if (db.sequence_count() > (int64_t)INT32_MAX) + output_mem(out, db, oid_to_centroid_oid); + else + output_mem(out, db, oid_to_centroid_oid); +} + +template +pair, vector> split(const vector& mapping) { + pair, vector> r; + r.second.reserve(mapping.size()); + for (Int i = 0; i < (Int)mapping.size(); ++i) + if (mapping[i] == i) + r.first.push_back(i); + else + r.second.push_back(i); + return r; +} + +template pair, vector> split(const vector&); +template pair, vector> split(const vector&); + +vector member_counts(const vector& mapping) { + vector v(mapping.size(), 0); + for (SuperBlockId c : mapping) + ++v[c]; + return v; +} + +void init_thresholds() { + if (!config.approx_min_id.present()) + config.approx_min_id = config.command == ::Config::DEEPCLUST ? 0.0 : 50.0; + if (config.soft_masking.empty()) + config.soft_masking = "tantan"; + if (!config.masking_.present()) + config.masking_ = "0"; + if (config.approx_min_id < 90.0) + return; + if(config.command == ::Config::CLUSTER_REASSIGN) { + config.diag_filter_id = 80.0; + config.diag_filter_cov = config.member_cover > 50.0 ? config.member_cover - 10.0 : 0.0; + } + else { + config.diag_filter_id = 85.0; + config.diag_filter_cov = config.member_cover > 50.0 ? config.member_cover - 5.0 : 0.0; + } +} + +File* open_out_tsv() { + File* file = new File(Schema{ Type::STRING, Type::STRING }, config.output_file, Flags::WRITE | Flags::OVERWRITE); + if (Blast_tab_format::header_format(::Config::cluster) == Header::SIMPLE) + file->write_record("centroid", "member"); + return file; +} + +vector len_sorted_clust(const FlatArray>& edges) { + vector v(edges.size(), -1); + for (int64_t i = 0; i < edges.size(); ++i) { + if (v[i] != -1) + continue; + v[i] = i; + for (auto it = edges.cbegin(i); it != edges.cend(i); ++it) + if (v[it->node2] == -1) + v[it->node2] = i; + } + return v; +} + +} \ No newline at end of file diff --git a/src/cluster/incremental/common.h b/src/cluster/incremental/common.h new file mode 100644 index 000000000..305076e22 --- /dev/null +++ b/src/cluster/incremental/common.h @@ -0,0 +1,36 @@ +#pragma once +#include "../../data/sequence_file.h" +#include "../search/search.h" +#include "../../data/fasta/fasta_file.h" +#include "../util/tsv/file.h" + +namespace Cluster { namespace Incremental { + +struct Config { + Config(); + ~Config(); + MessageStream message_stream; + int verbosity; + std::vector sens; + double block_size; + std::unique_ptr output_format; + std::unique_ptr db; + std::shared_ptr centroids; + std::unique_ptr output_file; + task_timer total_time; + int64_t seqs_processed; + int64_t letters_processed; + std::vector oid2centroid; + std::vector centroid2oid; + std::vector> cache; + int64_t time_self_aln; + std::vector time_search; + std::vector problem_size; + int64_t problem_size_self; + + void status_msg(); + void save_state(); + void load_state(); +}; + +}} \ No newline at end of file diff --git a/src/cluster/incremental/config.cpp b/src/cluster/incremental/config.cpp new file mode 100644 index 000000000..bb4e56691 --- /dev/null +++ b/src/cluster/incremental/config.cpp @@ -0,0 +1,65 @@ +#include +#include "common.h" +#include "../../output/output_format.h" +#include "../cluster.h" + +using std::endl; + +namespace Cluster { namespace Incremental { + +Config::Config() : + message_stream(true), + verbosity(1), + sens(Search::cluster_sens.at(config.sensitivity)), + block_size(config.chunk_size == 0.0 ? Search::sensitivity_traits[0].at(config.sensitivity).default_block_size : config.chunk_size), + output_format(init_output(-1)), + db(SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NEED_LETTER_COUNT | SequenceFile::Flags::OID_TO_ACC_MAPPING)), + centroids(config.resume.empty() ? new FastaFile(config.output_file + ".centroids.faa", true, FastaFile::WriteAccess()) + : new FastaFile(config.resume + ".centroids.faa", false, FastaFile::WriteAccess())), + output_file(open_out_tsv()), + seqs_processed(0), + letters_processed(0), + oid2centroid(db->sequence_count()), + time_self_aln(0), + time_search(sens.size() + 1, 0), + problem_size(sens.size() + 1, 0), + problem_size_self(0) +{ + sens.push_back(config.sensitivity); + for (int i = 0; i < sens.size() - 1; ++i) + cache.emplace_back(new Block); +} + +Config::~Config() { +} + +void Config::status_msg() { + message_stream << "#Seqs=" << seqs_processed << " #Centroids=" << centroids->sequence_count() << " Time=" << total_time.seconds() << "s" << std::endl; +} + +void Config::save_state() { + std::ofstream out1(config.output_file + ".oid2centroid"); + for (OId i = 0; i < db->tell_seq(); ++i) + out1 << oid2centroid[i] << endl; + std::ofstream out2(config.output_file + ".centroid2oid"); + for (OId i : centroid2oid) + out2 << i << endl; +} + +void Config::load_state() { + std::ifstream in1(config.resume + ".oid2centroid"); + int64_t n = 0; + CentroidId i; + while (in1 >> i) + oid2centroid[n++] = i; + std::ifstream in2(config.resume + ".centroid2oid"); + OId oid; + while (in2 >> oid) + centroid2oid.push_back(oid); + this->message_stream << "Centroid count = " << centroid2oid.size() << endl; + this->message_stream << "Seeking to OId " << n << endl; + db->set_seqinfo_ptr(n); + seqs_processed += n; +} + +}} \ No newline at end of file diff --git a/src/cluster/incremental/incremental.h b/src/cluster/incremental/incremental.h new file mode 100644 index 000000000..e4e4f7e00 --- /dev/null +++ b/src/cluster/incremental/incremental.h @@ -0,0 +1,16 @@ +#pragma once +#include "../cluster.h" + +namespace Cluster { namespace Incremental { + +class Algo: public ClusteringAlgorithm { +public: + ~Algo(){}; + virtual void run() override; + std::string get_description() override; + static std::string get_key(){ + return "incremental"; + } +}; + +}} diff --git a/src/cluster/incremental/run.cpp b/src/cluster/incremental/run.cpp new file mode 100644 index 000000000..d3045d93d --- /dev/null +++ b/src/cluster/incremental/run.cpp @@ -0,0 +1,231 @@ +#include +#include "incremental.h" +#include "../basic/config.h" +#include "../../data/sequence_file.h" +#include "../../run/workflow.h" +#include "../../data/block/block_wrapper.h" +#include "../../util/algo/algo.h" +#include "common.h" +#include "../../util/table.h" +#include "../cascaded/cascaded.h" + +using std::unique_ptr; +using std::shared_ptr; +using std::vector; +using std::endl; +using std::pair; +using Util::Table; +using Cluster::Callback; + +namespace Cluster { namespace Incremental { + +using Groups = vector>; + +std::string Algo::get_description() { + return "Incremental clustering (default)"; +} + +struct BestCentroid : public Consumer, public vector { + BestCentroid(BlockId block_size): + vector(block_size, -1) + {} + virtual void consume(const char *ptr, size_t n) { + const char* end = ptr + n; + while (ptr < end) { + const auto edge = *(Output::Format::Edge::Data*)ptr; + ptr += sizeof(Output::Format::Edge::Data); + this->operator[](edge.query) = edge.target; + } + } + virtual void finalize() {} + virtual ~BestCentroid() = default; +}; + +static void self_align(Block& block, Config& cfg) { + using Edge = Util::Algo::Edge; + task_timer timer(("CLUSTER Searching " + std::to_string(block.seqs().size()) + " unaligned sequences").c_str(), cfg.message_stream); + shared_ptr neighbors(new Callback()); + shared_ptr unaligned_wrapper(new BlockWrapper(block)); + config.self = true; + config.max_target_seqs_ = INT64_MAX; + config.toppercent = 100; + config.sensitivity = cfg.sens.back(); + config.chunk_size = 10.0; + config.mapany = false; + config.query_or_target_cover = config.member_cover; + config.query_cover = 0; + Search::run(unaligned_wrapper, nullptr, neighbors); + cfg.time_self_aln += timer.seconds(); + const auto n = block.seqs().size(); + cfg.problem_size_self += int64_t(n) * int64_t(n - 1) / 2; + + if (cfg.verbosity >= 2) + timer.go("CLUSTER Computing clustering"); + + message_stream << "Finished search. #Edges: " << neighbors->count << endl; + timer.go("Allocating buffers"); + vector edges(neighbors->count); + timer.go("Loading edges"); + InputFile f(neighbors->edge_file); + f.read(edges.data(), neighbors->count); + f.close_and_delete(); + timer.go("Sorting edges"); + FlatArray edge_array = make_flat_array_dense(move(edges), n, config.threads_, Edge::GetKey()); + timer.finish(); + + vector c = len_sorted_clust(edge_array); //Util::Algo::greedy_vertex_cover(edge_array, nullptr, false); + + cfg.centroids->init_write(); + int64_t new_centroids = 0; + vector block2centroid(n); + + for (BlockId i = 0; i < n; ++i) { + if (c[i] == i) { + block2centroid[i] = cfg.centroids->sequence_count(); + cfg.centroids->write_seq(block.seqs()[i], block.ids()[i]); + cfg.centroid2oid.push_back(block.block_id2oid(i)); + ++new_centroids; + } + } + + for (BlockId i = 0; i < n; ++i) + cfg.oid2centroid[block.block_id2oid(i)] = block2centroid[c[i]]; + + timer.finish(); + if (cfg.verbosity >= 2) + cfg.message_stream << "CLUSTER added " << new_centroids << " new centroids, " << cfg.centroids->sequence_count() << " total." << endl; +} + +static void search_vs_centroids(Block& block, const int round, Config& cfg) { + if (cfg.verbosity >= 2) + cfg.message_stream << "CLUSTER searching vs. centroids sensitivity = " << to_string(cfg.sens[round]) + << " #sequences = " << block.seqs().size() << " , #centroids = " << cfg.centroids->sequence_count() << endl; + cfg.status_msg(); + task_timer timer(("Searching " + std::to_string(block.seqs().size()) + " against centroid sequences (" + to_string(cfg.sens[round]) + ")").c_str(), cfg.message_stream); + shared_ptr block_wrapper(new BlockWrapper(block)); + shared_ptr best_centroid(new BestCentroid(block.seqs().size())); + config.self = false; + config.max_target_seqs_ = 1; + config.toppercent = 100; + config.sensitivity = cfg.sens[round]; + config.chunk_size = std::max(block.seqs().letters() / 1e9 + 0.01, cfg.block_size); + config.query_or_target_cover = 0; + config.query_cover = config.member_cover; + //config.mapany = true; + cfg.centroids->set_seqinfo_ptr(0); + Search::run(cfg.centroids, block_wrapper, best_centroid); + cfg.time_search[round] += timer.seconds(); + cfg.problem_size[round] += block.seqs().size() * cfg.centroids->sequence_count(); + + if (cfg.verbosity >= 2) + timer.go("CLUSTER Assigning to clusters"); + int64_t clustered = 0; + Block unaligned; + for (BlockId i = 0; i < block.seqs().size(); ++i) { + const OId oid = block.block_id2oid(i); + cfg.oid2centroid[oid] = best_centroid->operator[](i); + if (best_centroid->operator[](i) == -1) + unaligned.push_back(block.seqs()[i], block.ids()[i], nullptr, oid, SequenceType::amino_acid, 1); + else + ++clustered; + } + unaligned.seqs().finish_reserve(); + timer.finish(); + if (cfg.verbosity >= 2) + cfg.message_stream << "CLUSTER " << clustered << " assigned to clusters, " << unaligned.seqs().size() << " unaligned." << endl; + + if (round + 1 < cfg.sens.size()) + cfg.cache[round]->append(unaligned); + else + self_align(unaligned, cfg); +} + +void Algo::run() { + config.database.require(); + Config cfg; + config.db_size = cfg.db->letters(); + if (!config.resume.empty()) + cfg.load_state(); + + task_timer timer("CLUSTER Opening the input file", cfg.message_stream); + const int64_t block_size = (int64_t)(cfg.block_size * 1e9), cache_limit = 0; // block_size; + config.output_format = { "edge" }; + unique_ptr block; + if (config.resume.empty()) { + block.reset(cfg.db->load_seqs(std::min(block_size, config.bootstrap_block))); + cfg.seqs_processed += block->seqs().size(); + cfg.letters_processed += block->seqs().letters(); + timer.finish(); + self_align(*block, cfg); + } + + for (;;) { + timer.go("CLUSTER Loading sequences"); + const int64_t load_size = (int64_t)cfg.centroids->letters() * config.centroid_factor; + //const int64_t load_size = cfg.letters_processed; + block.reset(cfg.db->load_seqs(std::min(block_size, load_size))); + cfg.seqs_processed += block->seqs().size(); + cfg.letters_processed += block->seqs().letters(); + timer.finish(); + if (block->empty()) + break; + + search_vs_centroids(*block, 0, cfg); + + for (int i = 0; i < cfg.cache.size(); ++i) + if (cfg.cache[i]->seqs().letters() >= std::min(cache_limit, (int64_t)cfg.centroids->letters())) { + cfg.cache[i]->seqs().finish_reserve(); + search_vs_centroids(*cfg.cache[i], i + 1, cfg); + cfg.cache[i].reset(new Block); + } + + if (cfg.verbosity >= 2) + timer.go("CLUSTER Freeing memory"); + block.reset(); + + if (config.timeout && cfg.total_time.seconds() >= config.timeout) { + cfg.message_stream << "Timeout reached. Next OId = " << cfg.db->tell_seq() << endl; + cfg.save_state(); + break; + } + } + + for (int i = 0; i < cfg.cache.size(); ++i) + if (cfg.cache[i]->seqs().letters() > 0) { + cfg.cache[i]->seqs().finish_reserve(); + search_vs_centroids(*cfg.cache[i], i + 1, cfg); + } + + timer.go("Generating output"); + //const Groups groups = Util::Algo::sort_by_value(cfg.oid2centroid.cbegin(), cfg.oid2centroid.cend(), config.threads_); + //TextBuffer buf; + //for (const auto& p : groups) { +// buf << cfg.centroid2oid[p.first] << '\t' << p.second << '\n'; + //cfg.output_file->write(buf.data(), buf.size()); + //buf.clear(); + //} + for (int64_t i = 0; i < cfg.oid2centroid.size(); ++i) + cfg.oid2centroid[i] = cfg.centroid2oid[cfg.oid2centroid[i]]; + output_mem(*cfg.output_file, *cfg.db, cfg.oid2centroid); + + timer.go("Closing the database"); + cfg.db->close(); + cfg.centroids->close(); + timer.finish(); + + Table table; + + table("Total time", cfg.total_time.seconds(), "s"); + table("Self alignment time", cfg.time_self_aln, "s"); + table("Input sequences", cfg.db->sequence_count()); + table("Number of clusters", cfg.centroids->sequence_count()); + + for (int i = 0; i < cfg.sens.size(); ++i) { + table("Time (" + to_string(cfg.sens[i]) + ")", cfg.time_search[i], "s"); + table("Problem size (" + to_string(cfg.sens[i]) + ")", cfg.problem_size[i]); + } + table("Problem size self-aln", cfg.problem_size_self); + cfg.message_stream << endl << table; +} + +}} \ No newline at end of file diff --git a/src/cluster/medoid.cpp b/src/cluster/medoid.cpp deleted file mode 100644 index 56ca41ab4..000000000 --- a/src/cluster/medoid.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/**** -DIAMOND protein aligner -Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. - Benjamin Buchfink - Eberhard Karls Universitaet Tuebingen - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -****/ - -#include -#include -#include -#include -#include -#include "../basic/config.h" -#include "../util/string/tokenizer.h" -#include "../util/log_stream.h" -#include "../data/reference.h" -#include "../run/workflow.h" -#include "../basic/statistics.h" -#include "../util/sequence/sequence.h" - -using std::string; -using std::endl; -using std::map; -using std::pair; -using std::vector; -using std::shared_ptr; - -struct ClusterDist : public Consumer { - virtual void consume(const char *ptr, size_t n) override { - int query, subject, count, score; - //double evalue; - const char *end = ptr + n; - while (ptr < end) { - //if (sscanf(ptr, "%i\t%i\n%n", &query, &subject, &count) != 2) - if (sscanf(ptr, "%i\t%i\t%i\n%n", &query, &subject, &score, &count) != 3) - throw runtime_error("Cluster format error."); - ptr += count; - //std::cout << query << '\t' << subject << '\t' << evalue << endl; - if (query != subject) { - sum[query] += score; - ++counts[query]; - } - } - } - map sum; - map counts; -}; - -size_t get_medoid(shared_ptr& db, const shared_ptr &filter, size_t n, SequenceSet *seqs) { - statistics.reset(); - config.command = Config::blastp; - config.no_self_hits = true; - config.output_format = { "6", "qnum", "snum", "score" }; - config.swipe_all = true; - config.max_evalue = 100.0; - //config.freq_sd = 0; - config.max_alignments = SIZE_MAX; - config.algo = Config::Algo::DOUBLE_INDEXED; - //config.ext = Config::swipe; - score_matrix.set_db_letters(1); - - shared_ptr d(new ClusterDist); - - Search::run(db, nullptr, d, filter); - - int max_i = -1; - uint64_t max_s = 0; - for (pair i : d->sum) { - //std::cout << i.first << '\t' << i.second << '\t' << (*seqs)[i.first].length() << endl; - //const double sum = i.second + (n - 1 - d.counts[i.first])*100.0; - if (i.second > max_s) { - max_s = i.second; - max_i = i.first; - } - } - //std::cout << "=============" << endl; - //return max_i == -1 ? std::find(filter.begin(), filter.end(), true) - filter.begin() : max_i; - return 0; -} - -int get_acc2idx(const string& acc, const map& acc2idx) { - static std::locale loc; - if (std::isdigit(acc[0], loc)) - return atoi(acc.c_str()); - else { - auto i = acc2idx.find(acc); - return i == acc2idx.end() ? -1 : (int)i->second; - } -} - -void get_medoids_from_tree() { - const size_t CLUSTER_COUNT = 1000; - //config.masking = false; - shared_ptr db(SequenceFile::auto_create(config.database)); - message_stream << "#Sequences: " << db->sequence_count() << endl; - size_t n = db->sequence_count(); - - SequenceSet *seqs; - StringSet *ids; - db->load_seqs(SIZE_MAX, true); - - map parent; - map acc2idx; - for (size_t i = 0; i < n; ++i) { - const string id = (*ids)[i]; - parent[i] = i; - acc2idx[id] = i; - acc2idx[std::to_string(i)] = i; - } - - TextInputFile tree_in(config.tree_file); - int p; - string c1, c2; - while (tree_in.getline(), !tree_in.eof() && n > CLUSTER_COUNT) { - Util::String::Tokenizer(tree_in.line, "\t") >> p >> c1 >> c2; - parent[acc2idx.find(c1) == acc2idx.end() ? atoi(c1.c_str()) : acc2idx[c1]] = p; - parent[acc2idx.find(c2) == acc2idx.end() ? atoi(c2.c_str()) : acc2idx[c2]] = p; - parent[p] = p; - --n; - } - - n = db->sequence_count(); - map> clusters; - for (pair i : parent) { - while (parent[parent[i.first]] != parent[i.first]) - parent[i.first] = parent[parent[i.first]]; - if(i.first < (int)n) - clusters[parent[i.first]].push_back(i.first); - } - - shared_ptr filter(new BitVector(db->sequence_count())); - OutputFile out(config.output_file); - for (const pair> &i : clusters) { - /*for (const string &acc : i.second) - std::cout << acc << ' '; - std::cout << endl;*/ - size_t medoid; - if (i.second.size() == 1) - medoid = i.second.front(); - else { - filter.reset(); - for (const int& acc : i.second) - filter->set(acc); - medoid = get_medoid(db, filter, i.second.size(), seqs); - } - const string id = string((*ids)[medoid]) + ' ' + std::to_string(i.second.size()); - Util::Seq::format((*seqs)[medoid], id.c_str(), nullptr, out, "fasta", amino_acid_traits); - } - out.close(); - - db->close(); - delete seqs; - delete ids; -} \ No newline at end of file diff --git a/src/cluster/multi_step_cluster.cpp b/src/cluster/multi_step_cluster.cpp deleted file mode 100644 index fc687a5f6..000000000 --- a/src/cluster/multi_step_cluster.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/**** -DIAMOND protein aligner -Copyright (C) 2013-2018 Benjamin Buchfink - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -****/ - -#include -#include "multi_step_cluster.h" -#include "../util/util.h" -#include "../util/sequence/sequence.h" - -using namespace std; - -namespace Workflow { namespace Cluster { - -string MultiStep::get_description() { - return "A greedy stepwise vortex cover algorithm"; -} - -BitVector MultiStep::rep_bitset(const vector ¢roid, const BitVector *superset) { - BitVector r(centroid.size()); - for (int c : centroid) - if (!superset || superset->get(c)) - r.set(c); - return r; -} - -vector MultiStep::cluster(shared_ptr& db, const shared_ptr& filter) { - statistics.reset(); - config.command = Config::blastp; - //config.no_self_hits = true; - //config.output_format = { "6", "qnum", "snum" }; - //config.output_format = { "6", "qnum", "snum", "qcovhsp", "scovhsp", "bitscore" }; - config.output_format = { "bin" }; - config.query_cover = 80; - config.subject_cover = 80; - config.algo = Config::Algo::DOUBLE_INDEXED; - //config.freq_sd = 0; - config.max_alignments = numeric_limits::max(); - - shared_ptr nb(new Neighbors(db->sequence_count())); - - Search::run(db, nullptr, nb, filter); - - /* - auto lo = nb.dSet.getListOfSets(); - for (auto& set : lo) { - cout << "set :"; - for (auto item : set) { - cout << " " << item; - } - cout << endl; - } - */ - - vector> connected = nb->dSet.getListOfSets(); - vector EdgSet(nb->number_edges.size()); - unordered_map components = find_connected_components(connected, EdgSet, nb->number_edges); - message_stream << "Number of connected components: " << components.size() << endl; - message_stream << "Average number of nodes per connected component: " << (double)nb->number_edges.size() / components.size() << endl; - - uint32_t large = max_element(components.begin(), components.end(), [](const pair& left, const pair& right) {return left.second.nodes < right.second.nodes; })-> second.nodes; - message_stream << "Largest connected component has " << large << " nodes." << endl; - - vector tmp_sets = mapping_comp_set(components); - - uint32_t number_sets = max_element(components.begin(), components.end(), [](const pair& left, const pair& right) {return left.second.set < right.second.set; })-> second.set; - message_stream << "Number of sets: " << number_sets + 1 << endl; - - - if (config.external) { - save_edges_external(nb->tempfiles, tmp_sets, components, EdgSet); - return cluster_sets(db->sequence_count(), tmp_sets); - } - - else { - return Util::Algo::greedy_vertex_cover(*nb); - } - -} - -void MultiStep::save_edges_external(vector& all_edges, vector& sorted_edges, const unordered_map& comp, const vector& s_index){ - - size_t count = 0; - uint32_t query; - uint32_t subject; - uint32_t result_set; - while (count < all_edges.size()) { - InputFile tmp_edges(*all_edges[count]); - while (true) { - try { - tmp_edges.read(query); - tmp_edges.read(subject); - result_set = comp.at(s_index[query]).set; - sorted_edges[result_set]->write(query); - sorted_edges[result_set]->write(subject); - } - catch (EndOfStream&) { - tmp_edges.close_and_delete(); - break; - } - } - count++; - } -} - -vector MultiStep::cluster_sets(const size_t nb_size, vector &sorted_edges){ - vector cluster(nb_size); - vector> tmp_neighbors(nb_size); - vectorcurr; - uint32_t query; - uint32_t subject; - - iota(cluster.begin(), cluster.end(), 0); - - for (size_t i = 0; i < sorted_edges.size(); i++) { - InputFile tmp(*sorted_edges[i]); - delete(sorted_edges[i]); - while (true) { - try { - tmp.read(query); - tmp.read(subject); - tmp_neighbors[query].push_back(subject); - } - catch (EndOfStream&) { - tmp.close_and_delete(); - break; - } - } - curr = Util::Algo::greedy_vertex_cover(tmp_neighbors); - - for (int i = 0; i < (int)curr.size(); i++) { - if (curr[i] != i) { - cluster[i] = curr[i]; - } - } - - tmp_neighbors.clear(); - tmp_neighbors.resize(nb_size); - } - - return cluster; -} - -unordered_map MultiStep::find_connected_components(const vector> &connected, vector &EdgSet, const vector & nedges){ - - unordered_map ne; - for (size_t i = 0; i < connected.size(); i++) { - for(uint32_t j : connected[i]){ - EdgSet[j] = i; - ++ne[i].nodes; - ne[i].edges += nedges[j]; - } - } - - return ne; -} - -vector MultiStep::mapping_comp_set(unordered_map& comp) { - vector > set; - vector size; - vector temp_set; - - - bool TooBig; - - for (auto& it : comp) { - TooBig = true; - for (size_t j = 0; j < set.size(); j++) { - if ((it.second.edges + size[j]) <= config.max_size_set) { - set[j].push_back(it.first); - size[j] += it.second.edges; - comp[it.first].set = j; - TooBig = false; - break; - } - } - if (TooBig || set.empty()) { - set.push_back({ it.first }); - size.push_back({ it.second.edges }); - comp[it.first].set = set.size() - 1; - if (config.external) { - temp_set.push_back(new TempFile()); - } - } - } - return temp_set; -} - - -void MultiStep::steps(BitVector& current_reps, BitVector& previous_reps, vector & current_centroids, vector & previous_centroids, int count) { - - if (count == 0) { - current_reps = rep_bitset(current_centroids); - } - else { - current_reps = rep_bitset(current_centroids, &previous_reps); - - for (size_t i = 0; i < current_centroids.size(); ++i) - if (!previous_reps.get(i)) - current_centroids[i] = current_centroids[previous_centroids[i]]; - } - - size_t n_rep_1 = previous_reps.one_count(); - size_t n_rep_2 = current_reps.one_count(); - - if (n_rep_1 == 0) { - n_rep_1 = current_centroids.size(); - } - - message_stream << "Clustering step " << count + 1 << " complete. #Input sequences: " << n_rep_1 << " #Clusters: " << n_rep_2 << endl; - - previous_centroids = move(current_centroids); - previous_reps = move(current_reps); -} - -void MultiStep::run() { - if (config.database == "") - throw runtime_error("Missing parameter: database file (--db/-d)"); - config.command = Config::makedb; - shared_ptr db(SequenceFile::auto_create(config.database)); - const size_t seq_count = db->sequence_count(); - - shared_ptr current_reps, previous_reps; - - vector current_centroids; - vector previous_centroids; - - for (size_t i = 0; i < config.cluster_steps.size(); i++) { - - config.sensitivity = from_string(config.cluster_steps[i]); - current_centroids = cluster(db, i == 0 ? nullptr : previous_reps); - steps(*current_reps, *previous_reps, current_centroids, previous_centroids, i); - } - - task_timer timer("Generating output"); - vector rep_block_id(seq_count); - db->set_seqinfo_ptr(0); - Block* block = db->load_seqs((size_t)1e11, true, previous_reps.get()); - for (size_t i = 0; i < block->seqs().size(); ++i) - rep_block_id[block->block_id2oid(i)] = (unsigned)i; - - ostream* out = config.output_file.empty() ? &cout : new ofstream(config.output_file.c_str()); - vector seq; - string id; - db->init_seq_access(); - //Hsp hsp; - out->precision(3); - - for (int i = 0; i < (int)db->sequence_count(); ++i) { - db->read_seq(seq, id); - const unsigned r = rep_block_id[previous_centroids[i]]; - (*out) << Util::Seq::seqid(id.c_str(), false) << '\t' - << Util::Seq::seqid(block->ids()[r], false) << '\n'; - /*if ((int)i == centroid2[i]) - (*out) << "100\t100\t100\t0" << endl; - else { - Masking::get().bit_to_hard_mask(seq.data(), seq.size(), n); - smith_waterman(sequence(seq), (*rep_seqs)[r], hsp); - (*out) << hsp.id_percent() << '\t' - << hsp.query_cover_percent((unsigned)seq.size()) << '\t' - << hsp.subject_cover_percent((unsigned)(*rep_seqs)[r].length()) << '\t' - << score_matrix.bitscore(hsp.score) << endl; - } */ - } - if (out != &cout) delete out; - delete block; - db->close(); - -} -}} diff --git a/src/cluster/multi_step_cluster.h b/src/cluster/multi_step_cluster.h deleted file mode 100644 index 85659d7e3..000000000 --- a/src/cluster/multi_step_cluster.h +++ /dev/null @@ -1,111 +0,0 @@ -/**** -DIAMOND protein aligner -Copyright (C) 2013-2018 Benjamin Buchfink - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -****/ - -#pragma once -#include -#include -#include -#include -#include -#include -#include "../util/system/system.h" -#include "../basic/config.h" -#include "../data/reference.h" -#include "../run/workflow.h" -#include "../util/io/consumer.h" -#include "../util/algo/algo.h" -#include "../basic/statistics.h" -#include "../util/log_stream.h" -#include "../dp/dp.h" -#include "../masking/masking.h" -#include "cluster.h" -#include -#include -#include "../util/io/temp_file.h" -#include "disjoint_set.h" - -namespace Workflow { namespace Cluster{ - - struct NodEdgSet { - uint32_t nodes; - size_t edges; - uint32_t set; - }; - -class MultiStep : public ClusteringAlgorithm { -private: - BitVector rep_bitset(const vector ¢roid, const BitVector *superset = nullptr); - vector cluster(std::shared_ptr& db, const std::shared_ptr& filter); - void save_edges_external(std::vector &all_edges,std::vector &sorted_edges, const std::unordered_map & comp, const vector& s_index); - vector cluster_sets(const size_t nb_size, vector &sorted_edges); - std::unordered_map find_connected_components(const std::vector> &connected, vector& EdgSet, const vector& nedges); - vector mapping_comp_set(std::unordered_map& comp); - void steps(BitVector& current_reps, BitVector& previous_reps, vector& current_centroids, vector& previous_centroids, int count); - -public: - ~MultiStep(){}; - void run(); - string get_description(); - static string get_key(){ - return "multi-step"; - } -}; - -struct Neighbors : public vector>, public Consumer { - Neighbors(size_t n) : vector>(n), smallest_index(n,0), number_edges(n,0), dSet(n){ - iota(smallest_index.begin(), smallest_index.end(), 0); - } - - vector smallest_index; - vector number_edges; - vector tempfiles; - size_t size; - LazyDisjointIntegralSet dSet; - - - virtual void consume(const char* ptr, size_t n) override { - const char* end = ptr + n; - - if (config.external) { - size += n; - if (tempfiles.empty() || size >= UINT32_MAX) { - tempfiles.push_back(new TempFile()); - size = 0; - } - tempfiles.back()->write(ptr, n); - } - - while (ptr < end) { - const uint32_t query = *(uint32_t*)ptr; - ptr += sizeof(uint32_t); - const uint32_t subject = *(uint32_t*)ptr; - ptr += sizeof(uint32_t); - - if (!config.external) { - (*this)[query].push_back(subject); - } - - dSet.merge(query, subject); - - ++number_edges[query]; - - } - } - -}; -}} diff --git a/src/cluster/output.cpp b/src/cluster/output.cpp new file mode 100644 index 000000000..a9e8bc6c1 --- /dev/null +++ b/src/cluster/output.cpp @@ -0,0 +1,190 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2021-2022 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#include +#include +#include "cluster.h" +#include "../util/algo/algo.h" +#include "../util/util.h" +#include "../util/algo/sort_helper.h" +#include "../util/parallel/thread_pool.h" +#include "../dp/dp.h" +#include "../output/output_format.h" +#include "../stats/hauser_correction.h" +#include "../output/output.h" +#include "../util/algo/merge_files.h" + +using std::vector; +using std::unique_ptr; +using std::endl; +using std::to_string; +using std::mutex; +using std::lock_guard; +using std::thread; +using std::make_pair; +using std::list; +using std::lower_bound; +using std::atomic; +using std::shared_ptr; +using std::function; +using std::string; + +namespace Cluster { + +struct Cfg { + Cfg(HspValues hsp_values, bool lazy_titles, const FlatArray& clusters, const vector& centroids, SequenceFile& db): + hsp_values(hsp_values), + lazy_titles(lazy_titles), + clusters(clusters), + centroids(centroids), + db(db) + {} + const HspValues hsp_values; + const bool lazy_titles; + const FlatArray& clusters; + const vector& centroids; + SequenceFile& db; + shared_ptr centroid_block, member_block; +}; + +static void align_centroid(CentroidId centroid, ReorderQueue& out, Statistics& stats, ThreadPool& tp, Cfg& cfg) { + DP::Targets dp_targets; + const OId centroid_oid = cfg.centroids[centroid]; + const BlockId centroid_id = cfg.centroid_block->oid2block_id(centroid_oid); + const Sequence centroid_seq = cfg.centroid_block->seqs()[centroid_id]; + auto begin = lower_bound(cfg.clusters.cbegin(centroid), cfg.clusters.cend(centroid), cfg.member_block->oid_begin()); + auto end = lower_bound(cfg.clusters.cbegin(centroid), cfg.clusters.cend(centroid), cfg.member_block->oid_end()); + + for (auto it = begin; it != end; ++it) { + const BlockId block_id = cfg.member_block->oid2block_id(*it); + const Sequence seq(cfg.member_block->seqs()[block_id]); + const int bin = DP::BandedSwipe::bin(cfg.hsp_values, centroid_seq.length(), 0, 0, (int64_t)seq.length() * (int64_t)centroid_seq.length(), 0, 0); + dp_targets[bin].emplace_back(seq, seq.length(), block_id); + } + + const Bias_correction cbs(centroid_seq); + const string centroid_seqid = cfg.lazy_titles ? cfg.db.seqid(centroid_oid) : cfg.centroid_block->ids()[centroid_id]; + DP::Params p{ centroid_seq, centroid_seqid.c_str(), Frame(0), centroid_seq.length(), config.comp_based_stats == 1 ? cbs.int8.data() : nullptr, DP::Flags::FULL_MATRIX, cfg.hsp_values, stats, &tp }; + list hsps = DP::BandedSwipe::swipe(dp_targets, p); + + TextBuffer* buf = new TextBuffer; + TypeSerializer s(*buf); + for (Hsp& hsp : hsps) { + s << HspContext(hsp, + centroid_id, + centroid_oid, + TranslatedSequence(centroid_seq), + centroid_seqid.c_str(), + cfg.member_block->block_id2oid(hsp.swipe_target), + cfg.member_block->seqs().length(hsp.swipe_target), + cfg.lazy_titles ? cfg.db.seqid(cfg.member_block->block_id2oid(hsp.swipe_target)).c_str() : cfg.member_block->ids()[hsp.swipe_target], + 0, + 0, + Sequence()); + } + out.push(centroid, buf); +} + +static InputFile* run_block_pair(CentroidId begin, Cfg& cfg) { + atomic next(begin); + TempFile out; + OutputWriter writer{ &out }; + output_sink.reset(new ReorderQueue(begin, writer)); + auto worker = [&](ThreadPool& tp) { + Statistics stats; + CentroidId i = next++; + if (i >= (CentroidId)cfg.centroids.size() || cfg.centroids[i] >= cfg.centroid_block->oid_end()) + return false; + align_centroid(i, *output_sink, stats, tp, cfg); + statistics += stats; + return true; + }; + ThreadPool tp(worker); + tp.run(config.threads_, true); + tp.join(); + InputFile* f = new InputFile(out); + return f; +} + +void realign(const FlatArray& clusters, const vector& centroids, SequenceFile& db, function& callback, HspValues hsp_values) { + const int64_t block_size = Util::String::interpret_number(config.memory_limit.get(DEFAULT_MEMORY_LIMIT)) / 2; + message_stream << "Block size: " << block_size << " byte." << endl; + db.set_seqinfo_ptr(0); + score_matrix.set_db_letters(config.db_size ? config.db_size : db.letters()); + OId centroid_offset = 0; + int n1 = 0; + task_timer timer; + Cfg cfg{ hsp_values, flag_any(db.format_flags(), SequenceFile::FormatFlags::TITLES_LAZY), clusters, centroids, db }; + + SequenceFile::LoadFlags flags = SequenceFile::LoadFlags::SEQS | SequenceFile::LoadFlags::CONVERT_ALPHABET | SequenceFile::LoadFlags::NO_CLOSE_WEAKLY; + if (!cfg.lazy_titles) + flags |= SequenceFile::LoadFlags::TITLES; + + while (centroid_offset < db.sequence_count()) { + timer.go("Loading centroid block"); + db.set_seqinfo_ptr(centroid_offset); + cfg.centroid_block.reset(db.load_seqs(block_size, nullptr, flags)); + centroid_offset = db.tell_seq(); + + db.set_seqinfo_ptr(0); + int n2 = 0; + const CentroidId begin = lower_bound(centroids.cbegin(), centroids.cend(), cfg.centroid_block->oid_begin()) - centroids.cbegin(), + end = lower_bound(centroids.cbegin(), centroids.cend(), cfg.centroid_block->oid_end()) - centroids.cbegin(); + timer.finish(); + log_stream << "Total centroids = " << end - begin << endl; + vector tmp; + for (;;) { + if (cfg.centroid_block->seqs().size() == db.sequence_count()) + cfg.member_block = cfg.centroid_block; + else { + timer.go("Loading member block"); + cfg.member_block.reset(db.load_seqs(block_size, nullptr, flags)); + } + if (cfg.member_block->empty()) + break; + timer.go("Processing centroid block " + to_string(n1 + 1) + ", member block " + to_string(n2 + 1)); + tmp.push_back(run_block_pair(begin, cfg)); + ++n2; + if (cfg.centroid_block->seqs().size() == db.sequence_count()) + break; + } + timer.go("Joining centroid block " + to_string(n1 + 1)); + + merge_sorted_files::iterator, decltype(callback)>(tmp.begin(), tmp.end(), callback); + for (InputFile* f : tmp) { + f->close_and_delete(); + delete f; + } + ++n1; + } + timer.finish(); + statistics.print(); +} + +void realign(const vector& clustering, SequenceFile& db, function& callback, HspValues hsp_values) { + task_timer timer("Finding clusters"); + FlatArray clusters; + vector centroids; + tie(clusters, centroids) = cluster_sorted(clustering); + timer.finish(); + realign(clusters, centroids, db, callback, hsp_values); +} + +} \ No newline at end of file diff --git a/src/cluster/realign.cpp b/src/cluster/realign.cpp new file mode 100644 index 000000000..44b7ac236 --- /dev/null +++ b/src/cluster/realign.cpp @@ -0,0 +1,83 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#include +#include "../basic/config.h" +#include "../util/io/text_input_file.h" +#include "../util/log_stream.h" +#include "../data/sequence_file.h" +#include "cluster.h" +#include "../basic/match.h" +#include "../output/output_format.h" + +using std::endl; +using std::vector; +using std::unique_ptr; +using std::function; +using std::runtime_error; +using std::string; + +static const vector DEFAULT_FORMAT = { "6", "qseqid", "sseqid", "approx_pident", "qstart", "qend", "sstart", "send", "evalue", "bitscore" }; + +namespace Cluster { + +void realign() { + config.database.require(); + config.clustering.require(); + + OutputFile out(config.output_file); + if (config.output_format.empty()) + config.output_format = DEFAULT_FORMAT; + unique_ptr output_format(get_output_format()); + if (output_format->code != OutputFormat::blast_tab) + throw runtime_error("The realign workflow only supports tabular output format."); + if (Blast_tab_format::header_format(Config::cluster) == Header::SIMPLE) + dynamic_cast(output_format.get())->output_header(out, true); + + task_timer timer("Opening the database"); + unique_ptr db(SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NEED_LETTER_COUNT | SequenceFile::Flags::ACC_TO_OID_MAPPING, SequenceFile::Metadata())); + score_matrix.set_db_letters(config.db_size ? config.db_size : db->letters()); + config.max_evalue = DBL_MAX; + timer.finish(); + message_stream << "#Database sequences: " << db->sequence_count() << ", #Letters: " << db->letters() << endl; + if (flag_any(db->format_flags(), SequenceFile::FormatFlags::TITLES_LAZY)) + db->init_random_access(0, 0, false); + + FlatArray clusters; + vector centroids; + tie(clusters, centroids) = read(config.clustering, *db, CentroidSorted()); + message_stream << "Found " << centroids.size() << " centroids, " << clusters.data_size() << " mappings in input file." << endl; + + TextBuffer buf; + function format_output([&buf, &out, &output_format](const HspContext& h) { + Output::Info info{ SeqInfo(), false, nullptr, buf, {} }; + info.query.title = h.query_title.c_str(); + output_format->print_match(h, info); + out.write(buf.data(), buf.size()); + buf.clear(); + }); + realign(clusters, centroids, *db, format_output, output_format->hsp_values); + + timer.go("Freeing memory"); + db->close(); + out.close(); +} + +} \ No newline at end of file diff --git a/src/cluster/reassign.cpp b/src/cluster/reassign.cpp new file mode 100644 index 000000000..7ee181407 --- /dev/null +++ b/src/cluster/reassign.cpp @@ -0,0 +1,93 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#include "../basic/config.h" +#include "../util/log_stream.h" +#include "cluster.h" +#include "../basic/statistics.h" +#include "../run/workflow.h" +#include "../data/fasta/fasta_file.h" +#include "cascaded/cascaded.h" + +using std::endl; +using std::shared_ptr; +using std::for_each; +using std::vector; +using std::tie; +using std::make_shared; +using std::unique_ptr; + +namespace Cluster { + +void reassign() { + config.database.require(); + config.clustering.require(); + message_stream << "Coverage cutoff: " << config.member_cover << '%' << endl; + + task_timer timer("Opening the database"); + shared_ptr db(SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NEED_LETTER_COUNT | SequenceFile::Flags::ACC_TO_OID_MAPPING | SequenceFile::Flags::OID_TO_ACC_MAPPING, SequenceFile::Metadata())); + config.db_size = db->letters(); + timer.finish(); + message_stream << "#Database sequences: " << db->sequence_count() << ", #Letters: " << db->letters() << endl; + unique_ptr out(open_out_tsv()); + + timer.go("Reading the input file"); + vector clustering = read(config.clustering, *db); + + timer.go("Finding centroids"); + vector centroids, members; + tie(centroids, members) = split(clustering); + + timer.go("Creating member database"); + shared_ptr member_db(db->sub_db(members.cbegin(), members.cend())); + member_db->set_seqinfo_ptr(0); + + timer.go("Creating centroid database"); + shared_ptr centroid_db(db->sub_db(centroids.cbegin(), centroids.cend())); + centroid_db->set_seqinfo_ptr(0); + timer.finish(); + + statistics.reset(); + init_thresholds(); + config.command = Config::blastp; + config.max_target_seqs_ = 1; + config.output_format = { "edge" }; + config.self = false; + config.query_cover = config.member_cover; + config.sensitivity = from_string(cluster_steps(config.approx_min_id).back()); + shared_ptr mapback = make_shared(members.size()); + Search::run(centroid_db, member_db, mapback); + + timer.go("Updating clustering"); + const int64_t n = update_clustering(clustering.begin(), mapback->centroid_id.cbegin(), members.cbegin(), members.cend(), centroids.cbegin()); + timer.finish(); + + message_stream << "Reassigned members: " << n << '/' << members.size() << endl; + + timer.go("Generating output"); + if (flag_any(db->format_flags(), SequenceFile::FormatFlags::TITLES_LAZY)) + db->init_random_access(0, 0, false); + output_mem(*out, *db, clustering); + + timer.go("Closing the database"); + db.reset(); +} + +} \ No newline at end of file diff --git a/src/contrib/mcl/dense.h b/src/contrib/mcl/dense.h new file mode 100644 index 000000000..ae092d511 --- /dev/null +++ b/src/contrib/mcl/dense.h @@ -0,0 +1,111 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2020 QIAGEN A/S (Aarhus, Denmark) +Code developed by Patrick Ettenhuber + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once + +namespace Workflow { namespace Cluster{ + +void MCL::get_exp(Eigen::MatrixXf* in, Eigen::MatrixXf* out, float r){ + // TODO: at some r it may be more beneficial to diagnoalize in and only take the exponents of the eigenvalues + high_resolution_clock::time_point t = high_resolution_clock::now(); + if(r - (int) r == 0){ + *out = *in; + for(uint32_t i=1; ipow(r) + dense_int_exp_time += duration_cast(high_resolution_clock::now() - t).count(); + } + else{ + // TODO: check whether the matrix is self-adjoint and use SelfAdjointEigenSolver instead + // TODO: try and get out->noalias() = in->pow(r); to work (unsupported! http://eigen.tuxfamily.org/dox/unsupported/group__MatrixFunctions__Module.html). + Eigen::EigenSolver solver(*in); + Eigen::MatrixXcf d = solver.eigenvalues().asDiagonal(); + for(uint32_t idiag = 0; idiag numeric_limits::epsilon() ){ + out->noalias() = (V * d.real() * V.inverse()).real(); + } + dense_gen_exp_time += duration_cast(high_resolution_clock::now() - t).count(); + } +} + +void MCL::get_gamma(Eigen::SparseMatrix* in, Eigen::SparseMatrix* out, float r, uint32_t nThr) { + high_resolution_clock::time_point t = high_resolution_clock::now(); + vector> data; + std::mutex m; + auto mult = [&](const uint32_t iThr) { + vector> t_data = sparse_matrix_get_gamma(in, r, iThr, nThr); + m.lock(); + data.insert(data.end(), t_data.begin(), t_data.end()); + m.unlock(); + }; + + vector threads; + for (uint32_t iThread = 0; iThread < nThr; iThread++) { + threads.emplace_back(mult, iThread); + } + + for (uint32_t iThread = 0; iThread < nThr; iThread++) { + threads[iThread].join(); + } + out->setZero(); + out->setFromTriplets(data.begin(), data.end(), [](const float&, const float &b) { return b; }); + *out = out->pruned(1.0, numeric_limits::epsilon()); + sparse_gamma_time += duration_cast(high_resolution_clock::now() - t).count(); +} + +void MCL::get_gamma(Eigen::MatrixXf* in, Eigen::MatrixXf* out, float r) { + high_resolution_clock::time_point t = high_resolution_clock::now(); + // Note that Eigen matrices are column-major, so this is the most efficient way + for (uint32_t icol = 0; icol < in->cols(); ++icol) { + float colSum = 0.0f; + for (uint32_t irow = 0; irow < in->rows(); ++irow) { + colSum += pow(in->coeffRef(irow, icol), r); + } + for (uint32_t irow = 0; irow < in->rows(); ++irow) { + out->coeffRef(irow, icol) = pow(in->coeffRef(irow, icol), r) / colSum; + } + } + dense_gamma_time += duration_cast(high_resolution_clock::now() - t).count(); +} + +void MCL::markov_process(Eigen::MatrixXf* m, float inflation, float expansion, uint32_t max_iter) { + uint32_t iteration = 0; + float diff_norm = numeric_limits::max(); + Eigen::MatrixXf msquared(m->rows(), m->cols()); + Eigen::MatrixXf m_update(m->rows(), m->cols()); + get_gamma(m, m, 1); // This is to get a matrix of random walks on the graph -> TODO: find out if something else is more suitable + while (iteration < max_iter && diff_norm > numeric_limits::epsilon()) { + get_exp(m, &msquared, expansion); + get_gamma(&msquared, &m_update, inflation); + *m -= m_update; + diff_norm = m->norm(); + *m = m_update; + iteration++; + } + if (iteration == max_iter) { + failed_to_converge++; + } +} + +}} \ No newline at end of file diff --git a/src/cluster/mcl.cpp b/src/contrib/mcl/mcl.cpp similarity index 68% rename from src/cluster/mcl.cpp rename to src/contrib/mcl/mcl.cpp index 1e4516f1d..f88fefe47 100644 --- a/src/cluster/mcl.cpp +++ b/src/contrib/mcl/mcl.cpp @@ -32,6 +32,8 @@ along with this program. If not, see . #define MASK_ATTRACTOR_NODE 0x8000000000000000 #define MASK_SINGLE_NODE 0xC000000000000000 +const double DEFAULT_CLUSTERING_THRESHOLD = 50; + using std::thread; using std::numeric_limits; using std::chrono::high_resolution_clock; @@ -51,231 +53,20 @@ using std::ostream; using std::move; using std::max; +#include "sparse.h" +#include "dense.h" + namespace Workflow { namespace Cluster{ string MCL::get_description() { return "Markov clustering according to doi:10.1137/040608635"; } -vector> MCL::sparse_matrix_multiply(Eigen::SparseMatrix* a, Eigen::SparseMatrix* b , uint32_t iThr, uint32_t nThr){ - uint32_t n_cols = b->cols(); - uint32_t n_rows = a->rows(); - std::vector result_col(n_rows, 0.0); - vector> data; - for (uint32_t j=0; j::InnerIterator rhsIt(*b, j); rhsIt; ++rhsIt) { - const float y = rhsIt.value(); - const uint32_t k = rhsIt.row(); - for (Eigen::SparseMatrix::InnerIterator lhsIt(*a, k); lhsIt; ++lhsIt) { - const uint32_t i = lhsIt.row(); - const float x = lhsIt.value(); - result_col[i] += x*y; - } - } - for (uint32_t i=0; i numeric_limits::epsilon()) { - data.emplace_back(i, j, result_col[i]); - } - } - } - } - return data; -} - -void MCL::get_exp(Eigen::SparseMatrix* in, Eigen::SparseMatrix* out, float r, uint32_t nThr){ - high_resolution_clock::time_point t = high_resolution_clock::now(); - if( r - (int) r == 0 ){ - // TODO: at some r it may be more beneficial to diagnoalize in and only take the exponents of the eigenvalues - *out = *in; - for(uint32_t i=1; i> data; - std::mutex m; - auto mult = [&](const uint32_t iThr){ - vector> t_data = sparse_matrix_multiply(in, out, iThr, nThr); - m.lock(); - data.insert(data.end(), t_data.begin(), t_data.end()); - m.unlock(); - }; - - vector threads; - for(uint32_t iThread = 0; iThread < nThr ; iThread++) { - threads.emplace_back(mult, iThread); - } - - for(uint32_t iThread = 0; iThread < nThr ; iThread++) { - threads[iThread].join(); - } - out->setZero(); - out->setFromTriplets(data.begin(), data.end(), [] (const float&, const float &b) { return b; }); - *out = out->pruned(1.0, numeric_limits::epsilon()); - //(*out) = (*out)*(*in); - } - } - else{ - throw runtime_error(" Eigen does not provide an eigenvalue solver for sparse matrices"); - } - *out = out->pruned(); - sparse_exp_time += duration_cast(high_resolution_clock::now() - t).count(); -} - -void MCL::get_exp(Eigen::MatrixXf* in, Eigen::MatrixXf* out, float r){ - // TODO: at some r it may be more beneficial to diagnoalize in and only take the exponents of the eigenvalues - high_resolution_clock::time_point t = high_resolution_clock::now(); - if(r - (int) r == 0){ - *out = *in; - for(uint32_t i=1; ipow(r) - dense_int_exp_time += duration_cast(high_resolution_clock::now() - t).count(); - } - else{ - // TODO: check whether the matrix is self-adjoint and use SelfAdjointEigenSolver instead - // TODO: try and get out->noalias() = in->pow(r); to work (unsupported! http://eigen.tuxfamily.org/dox/unsupported/group__MatrixFunctions__Module.html). - Eigen::EigenSolver solver(*in); - Eigen::MatrixXcf d = solver.eigenvalues().asDiagonal(); - for(uint32_t idiag = 0; idiag numeric_limits::epsilon() ){ - out->noalias() = (V * d.real() * V.inverse()).real(); - } - dense_gen_exp_time += duration_cast(high_resolution_clock::now() - t).count(); - } -} - - -vector> MCL::sparse_matrix_get_gamma(Eigen::SparseMatrix* in, float r, uint32_t iThr, uint32_t nThr){ - vector> data; - for (uint32_t k=0; kouterSize(); ++k){ - if( k%nThr == iThr){ - float colSum = 0.0f; - for (Eigen::SparseMatrix::InnerIterator it(*in, k); it; ++it){ - colSum += pow(it.value(),r); - } - for (Eigen::SparseMatrix::InnerIterator it(*in, k); it; ++it){ - float val = pow(it.value(), r) / colSum; - if(abs(val) > numeric_limits::epsilon()) { - data.emplace_back(it.row(), it.col(), val); - } - } - } - } - return data; -} - -void MCL::get_gamma(Eigen::SparseMatrix* in, Eigen::SparseMatrix* out, float r, uint32_t nThr){ - high_resolution_clock::time_point t = high_resolution_clock::now(); - vector> data; - std::mutex m; - auto mult = [&](const uint32_t iThr){ - vector> t_data = sparse_matrix_get_gamma(in, r, iThr, nThr); - m.lock(); - data.insert(data.end(), t_data.begin(), t_data.end()); - m.unlock(); - }; - - vector threads; - for(uint32_t iThread = 0; iThread < nThr ; iThread++) { - threads.emplace_back(mult, iThread); - } - - for(uint32_t iThread = 0; iThread < nThr ; iThread++) { - threads[iThread].join(); - } - out->setZero(); - out->setFromTriplets(data.begin(), data.end(), [] (const float&, const float &b) { return b; }); - *out = out->pruned(1.0, numeric_limits::epsilon()); - sparse_gamma_time += duration_cast(high_resolution_clock::now() - t).count(); -} - -float MCL::sparse_matrix_get_norm(Eigen::SparseMatrix* in, uint32_t nThr){ - vector data(nThr); - fill(data.begin(), data.end(), 0.0); - auto norm = [&](const uint32_t iThr){ - for (uint32_t k=0; kouterSize(); ++k){ - if( k%nThr == iThr){ - for (Eigen::SparseMatrix::InnerIterator it(*in, k); it; ++it){ - data[iThr] += pow(it.value(),2.0); - } - } - } - }; - - vector threads; - for(uint32_t iThread = 0; iThread < nThr ; iThread++) { - threads.emplace_back(norm, iThread); - } - - for(uint32_t iThread = 0; iThread < nThr ; iThread++) { - threads[iThread].join(); - } - return pow(accumulate(data.begin(), data.end(), 0.0f), 0.5f); -} - -void MCL::get_gamma(Eigen::MatrixXf* in, Eigen::MatrixXf* out, float r){ - high_resolution_clock::time_point t = high_resolution_clock::now(); - // Note that Eigen matrices are column-major, so this is the most efficient way - for (uint32_t icol=0; icolcols(); ++icol){ - float colSum = 0.0f; - for (uint32_t irow=0; irowrows(); ++irow){ - colSum += pow(in->coeffRef(irow, icol) , r); - } - for (uint32_t irow=0; irowrows(); ++irow){ - out->coeffRef(irow, icol) = pow(in->coeffRef(irow, icol) , r) / colSum; - } - } - dense_gamma_time += duration_cast(high_resolution_clock::now() - t).count(); -} - -void MCL::markov_process(Eigen::SparseMatrix* m, float inflation, float expansion, uint32_t max_iter, function getThreads){ - uint32_t iteration = 0; - float diff_norm = numeric_limits::max(); - Eigen::SparseMatrix msquared(m->rows(), m->cols()); - Eigen::SparseMatrix m_update(m->rows(), m->cols()); - get_gamma(m, m, 1, getThreads()); // This is to get a matrix of random walks on the graph -> TODO: find out if something else is more suitable - while( iteration < max_iter && diff_norm > numeric_limits::epsilon()){ - get_exp(m, &msquared, expansion, getThreads()); - get_gamma(&msquared, &m_update, inflation, getThreads()); - *m -= m_update; - diff_norm = sparse_matrix_get_norm(m, getThreads()); - *m = m_update; - iteration++; - } - if( iteration == max_iter ){ - failed_to_converge++; - } -} - -void MCL::markov_process(Eigen::MatrixXf* m, float inflation, float expansion, uint32_t max_iter){ - uint32_t iteration = 0; - float diff_norm = numeric_limits::max(); - Eigen::MatrixXf msquared(m->rows(), m->cols()); - Eigen::MatrixXf m_update(m->rows(), m->cols()); - get_gamma(m, m, 1); // This is to get a matrix of random walks on the graph -> TODO: find out if something else is more suitable - while( iteration < max_iter && diff_norm > numeric_limits::epsilon()){ - get_exp(m, &msquared, expansion); - get_gamma(&msquared, &m_update, inflation); - *m -= m_update; - diff_norm = m->norm(); - *m = m_update; - iteration++; - } - if( iteration == max_iter ){ - failed_to_converge++; - } -} - -void MCL::print_stats(uint64_t nElements, uint32_t nComponents, uint32_t nComponentsLt1, vector& sort_order, vector>& indices, SparseMatrixStream* ms){ +void MCL::print_stats(int64_t nElements, int64_t nComponents, int64_t nComponentsLt1, vector& sort_order, vector>& indices, SparseMatrixStream* ms){ task_timer timer; timer.go("Collecting stats"); const uint32_t chunk_size = config.cluster_mcl_chunk_size; - const uint32_t nThreads = min(config.threads_, nComponents / chunk_size); + const int nThreads = min(config.threads_, int(nComponents / chunk_size)); const float inflation = (float) config.cluster_mcl_inflation; const float expansion = (float) config.cluster_mcl_expansion; @@ -283,30 +74,30 @@ void MCL::print_stats(uint64_t nElements, uint32_t nComponents, uint32_t nCompon vector neighbors(nComponentsLt1); vector memories(nComponentsLt1); - uint32_t max_job_size = 0; + int64_t max_job_size = 0; for(uint32_t i=0; iallocate_read_buffer(1); while(my_counter < nComponentsLt1){ unordered_set all; - uint32_t upper_limit = min(my_counter+my_chunk_size, nComponentsLt1); - vector*> loc_i; - for(uint32_t chunk_counter = my_counter; chunk_counter*> loc_i; + for (Id chunk_counter = my_counter; chunk_counter < upper_limit; chunk_counter++) { loc_i.push_back(&indices[sort_order[chunk_counter]]); } - vector>> loc_c = ms->collect_components(&loc_i, 0); + vector>> loc_c = ms->collect_components(&loc_i, 0); uint32_t ichunk = 0; - generate(sparsities.begin()+my_counter, sparsities.begin()+upper_limit, [my_counter, ichunk, &sort_order, &indices, &loc_c]() mutable { + generate(sparsities.begin()+my_counter, sparsities.begin()+upper_limit, [my_counter, ichunk, &sort_order, &indices, &loc_c]() mutable -> float { const uint32_t iComponent = sort_order[my_counter++]; - const uint32_t ssize = loc_c[ichunk++].size(); + const int64_t ssize = loc_c[ichunk++].size(); const uint64_t dsize = indices[iComponent].size() * indices[iComponent].size(); - return 1.0-(1.0 * ssize) / dsize; + return 1.0f - (1.0f * ssize) / dsize; }); - generate(neighbors.begin()+my_counter, neighbors.begin()+upper_limit, [my_counter, ichunk, &sort_order, &indices, &loc_c]() mutable { + generate(neighbors.begin()+my_counter, neighbors.begin()+upper_limit, [my_counter, ichunk, &sort_order, &indices, &loc_c]() mutable -> float { uint32_t neighbors = 0; const uint32_t iComponent = sort_order[my_counter++]; for(auto t: loc_c[ichunk++]){ @@ -332,19 +123,19 @@ void MCL::print_stats(uint64_t nElements, uint32_t nComponents, uint32_t nCompon sort(memories.rbegin(), memories.rend()); float mem_req = nElements * (2 * sizeof(uint32_t) + sizeof(float)); - for(uint32_t iThr = 0; iThr < nThreads; iThr++){ + for(int iThr = 0; iThr < nThreads; iThr++){ mem_req += 3*memories[iThr]; // we need three matrices of this size } float neighbors_of_max = neighbors[0]; float neighbors_of_med = nComponentsLt1 > 0 && nComponentsLt1 % 2 == 0 ? - (neighbors[nComponentsLt1/2] + neighbors[nComponentsLt1/2+1]) / 2.0 : + (neighbors[nComponentsLt1/2] + neighbors[nComponentsLt1/2+1]) / 2.0f : neighbors[nComponentsLt1/2+1] ; float neighbors_of_min = neighbors[nComponentsLt1-1]; float sparsity_of_max = sparsities[0]; float sparsity_of_med = nComponentsLt1 > 0 && nComponentsLt1 % 2 == 0 ? - (sparsities[nComponentsLt1/2] + sparsities[nComponentsLt1/2+1]) / 2.0 : + (sparsities[nComponentsLt1/2] + sparsities[nComponentsLt1/2+1]) / 2.0f : sparsities[nComponentsLt1/2+1] ; float sparsity_of_min = sparsities[nComponentsLt1-1]; @@ -352,16 +143,16 @@ void MCL::print_stats(uint64_t nElements, uint32_t nComponents, uint32_t nCompon sort(neighbors.rbegin(), neighbors.rend()); float med_sparsity = nComponentsLt1 > 0 && nComponentsLt1 % 2 == 0 ? - (sparsities[nComponentsLt1/2] + sparsities[nComponentsLt1/2+1]) / 2.0 : + (sparsities[nComponentsLt1/2] + sparsities[nComponentsLt1/2+1]) / 2.0f : sparsities[nComponentsLt1/2+1] ; float med_neighbors = nComponentsLt1 > 0 && nComponentsLt1 % 2 == 0 ? - (neighbors[nComponentsLt1/2] + neighbors[nComponentsLt1/2+1]) / 2.0 : + (neighbors[nComponentsLt1/2] + neighbors[nComponentsLt1/2+1]) / 2.0f : neighbors[nComponentsLt1/2+1] ; float median1 = nComponents > 0 && nComponents % 2 == 0 ? - (indices[sort_order[nComponents/2]].size() + indices[sort_order[nComponents/2+1]].size()) / 2.0 : + (indices[sort_order[nComponents/2]].size() + indices[sort_order[nComponents/2+1]].size()) / 2.0f : indices[sort_order[nComponents/2+1]].size(); float median2 = nComponentsLt1 > 0 && nComponentsLt1 % 2 == 0 ? - (indices[sort_order[nComponentsLt1/2]].size() + indices[sort_order[nComponentsLt1/2+1]].size()) / 2.0 : + (indices[sort_order[nComponentsLt1/2]].size() + indices[sort_order[nComponentsLt1/2+1]].size()) / 2.0f : indices[sort_order[nComponentsLt1/2+1]].size(); timer.finish(); @@ -391,27 +182,35 @@ shared_ptr> get_graph_handle(shared_ptr& if(config.cluster_restart){ task_timer timer; timer.go("Reading cluster checkpoint file"); - shared_ptr> ms(SparseMatrixStream::fromFile(symmetric, config.cluster_graph_file, config.chunk_size)); + shared_ptr> ms(SparseMatrixStream::fromFile(symmetric, config.cluster_graph_file, (float)config.chunk_size)); timer.finish(); ms->done(); return ms; } config.command = Config::blastp; config.no_self_hits = false; + config.max_target_seqs_.set_if_blank(INT64_MAX); string format = config.cluster_similarity; - if(format.empty()){ - format = "qcovhsp/100*scovhsp/100*pident/100"; + if (format.empty()) { + format = "normalized_bitscore_global"; + config.cluster_threshold.set_if_blank(DEFAULT_CLUSTERING_THRESHOLD); + if (config.cluster_threshold == 0.0) + config.cluster_threshold.unset(); } + else + if (config.cluster_threshold.blank()) + std::cerr << "WARNING: It is recommended to set a threshold value for the clustering similarity measure (option `--cluster-threshold`)." << std::endl; config.output_format = {"clus", format}; shared_ptr> ms(new SparseMatrixStream(symmetric, db->sequence_count(), config.cluster_graph_file)); if(config.chunk_size > 0){ - ms->set_max_mem(config.chunk_size); + ms->set_max_mem((float)config.chunk_size); } Search::run(db, nullptr, ms); ms->done(); return ms; } -Eigen::SparseMatrix MCL::get_sparse_matrix_and_clear(vector* order, vector>* m, bool symmetric){ + +Eigen::SparseMatrix MCL::get_sparse_matrix_and_clear(vector* order, vector>* m, bool symmetric){ Eigen::SparseMatrix m_sparse(order->size(), order->size()); if(symmetric){ uint64_t oSize = m->size(); @@ -426,7 +225,7 @@ Eigen::SparseMatrix MCL::get_sparse_matrix_and_clear(vector* or m->clear(); return m_sparse; } -Eigen::MatrixXf MCL::get_dense_matrix_and_clear(vector* order, vector>* m, bool symmetric){ +Eigen::MatrixXf MCL::get_dense_matrix_and_clear(vector* order, vector>* m, bool symmetric){ Eigen::MatrixXf m_dense = Eigen::MatrixXf::Zero(order->size(), order->size()); for(Eigen::Triplet const & t : *m){ m_dense(t.row(), t.col()) = t.value(); @@ -562,21 +361,21 @@ void MCL::run(){ // return; if (config.database.empty()) throw runtime_error("Missing parameter: database file (--db/-d)"); - shared_ptr db(SequenceFile::auto_create(config.database)); + shared_ptr db(SequenceFile::auto_create({ config.database })); statistics.reset(); shared_ptr> ms(get_graph_handle(db)); task_timer timer; timer.go("Computing independent components"); - vector> indices = ms->get_indices(); + vector> indices = ms->get_indices(); ms->clear_disjoint_set(); uint64_t nElements = ms->getNumberOfElements(); - uint32_t nComponents = count_if(indices.begin(), indices.end(), [](vector v){ return v.size() > 0;}); - uint32_t nComponentsLt1 = count_if(indices.begin(), indices.end(), [](vector v){ return v.size() > 1;}); + uint32_t nComponents = count_if(indices.begin(), indices.end(), [](vector v){ return v.size() > 0;}); + uint32_t nComponentsLt1 = count_if(indices.begin(), indices.end(), [](vector v){ return v.size() > 1;}); // Sort to get the largest chunks first - vector sort_order(indices.size()); + vector sort_order(indices.size()); iota(sort_order.begin(), sort_order.end(), 0); - sort(sort_order.begin(), sort_order.end(), [&](uint32_t i, uint32_t j){return indices[i].size() > indices[j].size();}); + sort(sort_order.begin(), sort_order.end(), [&](int64_t i, int64_t j){return indices[i].size() > indices[j].size();}); timer.finish(); if(config.cluster_mcl_stats){ print_stats(nElements, nComponents, nComponentsLt1, sort_order, indices, ms.get()); @@ -590,14 +389,14 @@ void MCL::run(){ // note that the disconnected components are sorted by size const uint32_t chunk_size = config.cluster_mcl_chunk_size; - const uint32_t nThreads = min(config.threads_, nComponents / chunk_size); + const int nThreads = min(config.threads_, int(nComponents / chunk_size)); const uint32_t exessThreads = config.threads_ - nThreads; ms->allocate_read_buffer(nThreads); const float inflation = (float) config.cluster_mcl_inflation; const float expansion = (float) config.cluster_mcl_expansion; - const uint32_t max_counter = nComponents; + const int64_t max_counter = nComponents; const uint32_t max_iter = config.cluster_mcl_max_iter; - uint32_t max_job_size = 0; + int64_t max_job_size = 0; for(uint32_t i=0; i all; uint32_t upper_limit = min(my_counter+my_chunk_size, max_counter); - vector*> loc_i; + vector*> loc_i; for(uint32_t chunk_counter = my_counter; chunk_counter* order = loc_i[ichunk]; + vector* order = loc_i[ichunk]; if(order->size() > 1){ vector>* m = &loc_c[ichunk]; @@ -720,11 +519,11 @@ void MCL::run(){ }; vector threads; - for(uint32_t iThread = 0; iThread < nThreads ; iThread++) { + for(int iThread = 0; iThread < nThreads ; iThread++) { threads.emplace_back(mcl_clustering, iThread); } - for(uint32_t iThread = 0; iThread < nThreads ; iThread++) { + for(int iThread = 0; iThread < nThreads ; iThread++) { threads[iThread].join(); } ms->release_read_buffer(); @@ -732,12 +531,12 @@ void MCL::run(){ // Output stats message_stream << "Jobs per thread: "; - for(uint32_t iThread = 0; iThread < nThreads ; iThread++) { + for(int iThread = 0; iThread < nThreads ; iThread++) { message_stream << " " << setw(8) << right << jobs_per_thread[iThread]; } message_stream << endl; message_stream << "Time per thread: "; - for(uint32_t iThread = 0; iThread < nThreads ; iThread++) { + for(int iThread = 0; iThread < nThreads ; iThread++) { message_stream << " " << setw(8) << right << time_per_thread[iThread]; } message_stream << endl; @@ -785,7 +584,7 @@ void MCL::run(){ out->precision(3); for (int i = 0; i < (int)db->sequence_count(); ++i) { db->read_seq(seq, id); - const uint64_t cid = (~MASK_INVERSE) & clustering_result[i]; + const uint64_t cid = ((~MASK_INVERSE) & clustering_result[i]) + 1; const uint64_t lab = MASK_INVERSE & clustering_result[i]; (*out) << Util::Seq::seqid(id.c_str(), false) << '\t' ; switch(lab){ diff --git a/src/cluster/mcl.h b/src/contrib/mcl/mcl.h similarity index 84% rename from src/cluster/mcl.h rename to src/contrib/mcl/mcl.h index 662902038..8fc7a7675 100644 --- a/src/cluster/mcl.h +++ b/src/contrib/mcl/mcl.h @@ -36,24 +36,26 @@ along with this program. If not, see . #include "../basic/statistics.h" #include "../util/log_stream.h" #include "../dp/dp.h" -#include "cluster.h" +#include "../../cluster/cluster.h" #include "sparse_matrix_stream.h" namespace Workflow { namespace Cluster{ class MCL: public ClusteringAlgorithm { -private: +private: + using Weight = float; + using Id = SparseMatrixStream::Id; vector> sparse_matrix_multiply(Eigen::SparseMatrix* a, Eigen::SparseMatrix* b , uint32_t iThr, uint32_t nThr); vector> sparse_matrix_get_gamma(Eigen::SparseMatrix* in, float r, uint32_t iThr, uint32_t nThr); float sparse_matrix_get_norm(Eigen::SparseMatrix* in, uint32_t nThr); - void print_stats(uint64_t nElements, uint32_t nComponents, uint32_t nComponentsLt1, vector& sort_order, vector>& indices, SparseMatrixStream* ms); + void print_stats(int64_t nElements, int64_t nComponents, int64_t nComponentsLt1, vector& sort_order, vector>& indices, SparseMatrixStream* ms); void get_exp(Eigen::SparseMatrix* in, Eigen::SparseMatrix* out, float r, uint32_t nThr); void get_exp(Eigen::MatrixXf* in, Eigen::MatrixXf* out, float r); void get_gamma(Eigen::SparseMatrix* in, Eigen::SparseMatrix* out, float r, uint32_t nThr); void get_gamma(Eigen::MatrixXf* in, Eigen::MatrixXf* out, float r); void markov_process(Eigen::SparseMatrix* m, float inflation, float expansion, uint32_t max_iter, std::function getThreads); void markov_process(Eigen::MatrixXf* m, float inflation, float expansion, uint32_t max_iter); - Eigen::SparseMatrix get_sparse_matrix_and_clear(vector* order, vector>* m, bool symmetric); - Eigen::MatrixXf get_dense_matrix_and_clear(vector* order, vector>* m, bool symmetric); + Eigen::SparseMatrix get_sparse_matrix_and_clear(vector* order, vector>* m, bool symmetric); + Eigen::MatrixXf get_dense_matrix_and_clear(vector* order, vector>* m, bool symmetric); std::atomic_ullong failed_to_converge = {0}; std::atomic_ullong sparse_create_time = {0}; std::atomic_ullong dense_create_time = {0}; diff --git a/src/contrib/mcl/sparse.h b/src/contrib/mcl/sparse.h new file mode 100644 index 000000000..6d3197a3e --- /dev/null +++ b/src/contrib/mcl/sparse.h @@ -0,0 +1,152 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2020 QIAGEN A/S (Aarhus, Denmark) +Code developed by Patrick Ettenhuber + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once + +using std::runtime_error; + +namespace Workflow { namespace Cluster{ + +vector> MCL::sparse_matrix_multiply(Eigen::SparseMatrix* a, Eigen::SparseMatrix* b , uint32_t iThr, uint32_t nThr){ + int64_t n_cols = b->cols(); + int64_t n_rows = a->rows(); + std::vector result_col(n_rows, 0.0); + vector> data; + for (uint32_t j=0; j::InnerIterator rhsIt(*b, j); rhsIt; ++rhsIt) { + const float y = rhsIt.value(); + const int64_t k = rhsIt.row(); + for (Eigen::SparseMatrix::InnerIterator lhsIt(*a, k); lhsIt; ++lhsIt) { + const int64_t i = lhsIt.row(); + const float x = lhsIt.value(); + result_col[i] += x*y; + } + } + for (int64_t i=0; i numeric_limits::epsilon()) { + data.emplace_back(i, j, result_col[i]); + } + } + } + } + return data; +} + +void MCL::get_exp(Eigen::SparseMatrix* in, Eigen::SparseMatrix* out, float r, uint32_t nThr){ + high_resolution_clock::time_point t = high_resolution_clock::now(); + if( r - (int) r == 0 ){ + // TODO: at some r it may be more beneficial to diagnoalize in and only take the exponents of the eigenvalues + *out = *in; + for(uint32_t i=1; i> data; + std::mutex m; + auto mult = [&](const uint32_t iThr){ + vector> t_data = sparse_matrix_multiply(in, out, iThr, nThr); + m.lock(); + data.insert(data.end(), t_data.begin(), t_data.end()); + m.unlock(); + }; + + vector threads; + for(uint32_t iThread = 0; iThread < nThr ; iThread++) { + threads.emplace_back(mult, iThread); + } + + for(uint32_t iThread = 0; iThread < nThr ; iThread++) { + threads[iThread].join(); + } + out->setZero(); + out->setFromTriplets(data.begin(), data.end(), [] (const float&, const float &b) { return b; }); + *out = out->pruned(1.0, numeric_limits::epsilon()); + //(*out) = (*out)*(*in); + } + } + else{ + throw runtime_error("Eigen does not provide an eigenvalue solver for sparse matrices"); + } + *out = out->pruned(); + sparse_exp_time += duration_cast(high_resolution_clock::now() - t).count(); +} + +vector> MCL::sparse_matrix_get_gamma(Eigen::SparseMatrix* in, float r, uint32_t iThr, uint32_t nThr) { + vector> data; + for (uint32_t k = 0; k < in->outerSize(); ++k) { + if (k%nThr == iThr) { + float colSum = 0.0f; + for (Eigen::SparseMatrix::InnerIterator it(*in, k); it; ++it) { + colSum += pow(it.value(), r); + } + for (Eigen::SparseMatrix::InnerIterator it(*in, k); it; ++it) { + float val = pow(it.value(), r) / colSum; + if (abs(val) > numeric_limits::epsilon()) { + data.emplace_back(it.row(), it.col(), val); + } + } + } + } + return data; +} + +float MCL::sparse_matrix_get_norm(Eigen::SparseMatrix* in, uint32_t nThr) { + vector data(nThr); + fill(data.begin(), data.end(), 0.0); + auto norm = [&](const uint32_t iThr) { + for (uint32_t k = 0; k < in->outerSize(); ++k) { + if (k%nThr == iThr) { + for (Eigen::SparseMatrix::InnerIterator it(*in, k); it; ++it) { + data[iThr] += pow(it.value(), 2.0f); + } + } + } + }; + + vector threads; + for (uint32_t iThread = 0; iThread < nThr; iThread++) { + threads.emplace_back(norm, iThread); + } + + for (uint32_t iThread = 0; iThread < nThr; iThread++) { + threads[iThread].join(); + } + return pow(accumulate(data.begin(), data.end(), 0.0f), 0.5f); +} + +void MCL::markov_process(Eigen::SparseMatrix* m, float inflation, float expansion, uint32_t max_iter, function getThreads) { + uint32_t iteration = 0; + float diff_norm = numeric_limits::max(); + Eigen::SparseMatrix msquared(m->rows(), m->cols()); + Eigen::SparseMatrix m_update(m->rows(), m->cols()); + get_gamma(m, m, 1, getThreads()); // This is to get a matrix of random walks on the graph -> TODO: find out if something else is more suitable + while (iteration < max_iter && diff_norm > numeric_limits::epsilon()) { + get_exp(m, &msquared, expansion, getThreads()); + get_gamma(&msquared, &m_update, inflation, getThreads()); + *m -= m_update; + diff_norm = sparse_matrix_get_norm(m, getThreads()); + *m = m_update; + iteration++; + } + if (iteration == max_iter) { + failed_to_converge++; + } +} + +}} \ No newline at end of file diff --git a/src/cluster/sparse_matrix_stream.h b/src/contrib/mcl/sparse_matrix_stream.h similarity index 82% rename from src/cluster/sparse_matrix_stream.h rename to src/contrib/mcl/sparse_matrix_stream.h index 1a04c2cd7..7dee684b5 100644 --- a/src/cluster/sparse_matrix_stream.h +++ b/src/contrib/mcl/sparse_matrix_stream.h @@ -26,9 +26,9 @@ along with this program. If not, see . #include #include #include -#include "disjoint_set.h" +#include "../../util/data_structures/lazy_disjoint_set.h" #include "../util/io/consumer.h" -#include "cluster.h" +#include "../../cluster/cluster.h" using std::vector; using std::map; @@ -39,11 +39,19 @@ using std::ifstream; using std::min; using std::move; using std::max; +using std::runtime_error; +using std::string; namespace Workflow { namespace Cluster{ template class SparseMatrixStream : public Consumer { +public: + + using Id = OId; + +private: + size_t n; uint32_t nThreads; const static size_t unit_size = 2*sizeof(uint32_t)+sizeof(double); @@ -64,8 +72,8 @@ class SparseMatrixStream : public Consumer { }; std::set, bool(*)(const Eigen::Triplet& lhs,const Eigen::Triplet& rhs)>* data; //set, decltype(cmp)> data; - LazyDisjointSet* disjointSet; - string file_name; + LazyDisjointSet* disjointSet; + std::string file_name; std::ofstream* os; inline void write_triplet(const uint32_t* const query, const uint32_t* const subject, const T* const value){ if(os){ @@ -76,10 +84,10 @@ class SparseMatrixStream : public Consumer { } } - vector>> split_data(map& indexToSetId, size_t size){ + vector>> split_data(map& indexToSetId, size_t size){ vector>> split(size); for(Eigen::Triplet t : *data){ - uint32_t iset = indexToSetId[t.row()]; + Id iset = indexToSetId[t.row()]; assert( iset == indexToSetId[t.col()]); split[iset].emplace_back(t.row(), t.col(), t.value()); } @@ -89,20 +97,20 @@ class SparseMatrixStream : public Consumer { void dump(){ if(os && data->size() > 0){ this->in_memory = false; - vector> indices = get_indices(); - map indexToSetId; - for(uint32_t iset = 0; iset < indices.size(); iset++){ - for(uint32_t index : indices.at(iset)){ + vector> indices = get_indices(); + map indexToSetId; + for(Id iset = 0; iset < (Id)indices.size(); iset++){ + for(Id index : indices.at(iset)){ indexToSetId.emplace(index, iset); } } vector>> components = split_data(indexToSetId, indices.size()); for(uint32_t iComponent = 0; iComponent < components.size(); iComponent++){ - const uint32_t size = components[iComponent].size(); + const int32_t size = (int32_t)components[iComponent].size(); if( size > 0 ){ - const uint32_t first_index = indices[iComponent][0]; - os->write((char*) &first_index, sizeof(uint32_t)); - os->write((char*) &size, sizeof(uint32_t)); + const int64_t first_index = indices[iComponent][0]; + os->write((char*) &first_index, sizeof(int64_t)); + os->write((char*) &size, sizeof(int32_t)); for(auto& t : components[iComponent]){ const uint32_t irow = t.row(); const uint32_t icol = t.col(); @@ -131,7 +139,7 @@ class SparseMatrixStream : public Consumer { warned=true; } ptr += sizeof(double); - Eigen::Triplet t(query, subject, value); + Eigen::Triplet t(query, subject, (T)value); auto it = data->find(t); if(it == data->end()){ data->emplace(move(t)); @@ -170,7 +178,7 @@ class SparseMatrixStream : public Consumer { return os; } - vector> remap(vector>& split, map& index_map){ + vector> remap(vector>& split, map& index_map){ vector> remapped; for(Eigen::Triplet const & t : split){ remapped.emplace_back(index_map[t.row()], index_map[t.col()], t.value()); @@ -179,23 +187,23 @@ class SparseMatrixStream : public Consumer { return remapped; } - vector>> getComponents(vector*>* indices){ + vector>> getComponents(vector*>* indices){ vector>> split; { - map indexToSetId; - for(uint32_t iset = 0; iset < indices->size(); iset++){ - for(uint32_t index : *(indices->at(iset))){ + map indexToSetId; + for(Id iset = 0; iset < (Id)indices->size(); iset++){ + for(Id index : *(indices->at(iset))){ indexToSetId.emplace(index, iset); } } split = split_data(indexToSetId, indices->size()); } vector>> components; - for(uint32_t iset = 0; iset < indices->size(); iset++){ + for(Id iset = 0; iset < (Id)indices->size(); iset++){ if(split[iset].size() > 0){ - map index_map; - uint32_t iel = 0; - for(uint32_t const & el: *(indices->at(iset))){ + map index_map; + Id iel = 0; + for(Id const & el: *(indices->at(iset))){ index_map.emplace(el, iel++); } components.emplace_back(remap(split[iset], index_map)); @@ -204,24 +212,25 @@ class SparseMatrixStream : public Consumer { return components; } - SparseMatrixStream(const bool symmetric, size_t n): n(n), nThreads(0), symmetric(symmetric), in_memory(false), is_tmp_file(false), warned(false), max_size(2.0), buffer(nullptr), os(nullptr){ + SparseMatrixStream(const bool symmetric, int64_t n): n(n), nThreads(0), symmetric(symmetric), in_memory(false), is_tmp_file(false), warned(false), max_size(2.0), buffer(nullptr), os(nullptr){ bool(*function_pointer)(const Eigen::Triplet& lhs, const Eigen::Triplet& rhs) = symmetric ? symmcmp : cmp; data = new std::set, bool(*)(const Eigen::Triplet& lhs,const Eigen::Triplet& rhs)>(function_pointer); - disjointSet = new LazyDisjointIntegralSet(n); + disjointSet = new LazyDisjointIntegralSet(n); } - SparseMatrixStream(const bool symmetric, unordered_set* set) : nThreads(0), symmetric(symmetric), in_memory(true), is_tmp_file(false), warned(true), max_size(2.0), buffer(nullptr), os(nullptr){ + SparseMatrixStream(const bool symmetric, unordered_set* set) : nThreads(0), symmetric(symmetric), in_memory(true), is_tmp_file(false), warned(true), max_size(2.0), buffer(nullptr), os(nullptr){ bool(*function_pointer)(const Eigen::Triplet& lhs, const Eigen::Triplet& rhs) = symmetric ? symmcmp : cmp; data = new std::set, bool(*)(const Eigen::Triplet& lhs,const Eigen::Triplet& rhs)>(function_pointer); this->n = set->size(); - disjointSet = new LazyDisjointTypeSet(set); + disjointSet = new LazyDisjointTypeSet(set); } public: + SparseMatrixStream(const bool symmetric, size_t n, string graph_file_name) : n(n), nThreads(0), symmetric(symmetric), in_memory(false), warned(false), max_size(2.0), buffer(nullptr) { bool(*function_pointer)(const Eigen::Triplet& lhs, const Eigen::Triplet& rhs) = symmetric ? symmcmp : cmp; data = new std::set, bool(*)(const Eigen::Triplet& lhs,const Eigen::Triplet& rhs)>(function_pointer); - disjointSet = new LazyDisjointIntegralSet(n); + disjointSet = new LazyDisjointIntegralSet(n); if(graph_file_name.empty()){ this->is_tmp_file = true; file_name = "tmp.bin"; @@ -296,8 +305,8 @@ class SparseMatrixStream : public Consumer { if(max_size>0) sms->set_max_mem(max_size); char* local_buffer = new char[read_buffer_size]; while(in.good()){ - uint32_t first_component; - in.read((char*) &first_component, sizeof(uint32_t)); + int64_t first_component; + in.read((char*) &first_component, sizeof(int64_t)); uint32_t size; in.read((char*) &size, sizeof(uint32_t)); const unsigned long long block_size = size*unit_size; @@ -326,7 +335,7 @@ class SparseMatrixStream : public Consumer { return sms; } - vector>> collect_components(vector*>* indices, uint32_t iThread){ + vector>> collect_components(vector*>* indices, uint32_t iThread){ if(in_memory){ return getComponents(indices); } @@ -346,15 +355,15 @@ class SparseMatrixStream : public Consumer { throw runtime_error("This file cannot be read"); } - unordered_set set; - for(const vector* const idxs : *indices){ + unordered_set set; + for(const vector* const idxs : *indices){ set.insert(idxs->begin(), idxs->end()); } SparseMatrixStream sms(this->symmetric, &set); while(in.good()){ - uint32_t first_component; - in.read((char*) &first_component, sizeof(uint32_t)); + Id first_component; + in.read((char*) &first_component, sizeof(Id)); uint32_t size; in.read((char*) &size, sizeof(uint32_t)); const unsigned long long block_size = size*unit_size; @@ -376,9 +385,9 @@ class SparseMatrixStream : public Consumer { } } - vector> get_indices(){ - vector> sets = disjointSet->getListOfSets(); - vector> indices; + vector> get_indices(){ + vector> sets = disjointSet->getListOfSets(); + vector> indices; for(uint32_t iset = 0; iset < sets.size(); iset++){ indices.emplace_back(sets[iset].begin(), sets[iset].end()); } diff --git a/src/data/blastdb/blastdb.cpp b/src/data/blastdb/blastdb.cpp index 2ff34340b..27412f9c3 100644 --- a/src/data/blastdb/blastdb.cpp +++ b/src/data/blastdb/blastdb.cpp @@ -22,12 +22,14 @@ along with this program. If not, see . #include #include #include +#include #include #include "../util/io/text_input_file.h" #include "blastdb.h" #include "../../util/string/tokenizer.h" #include "../../util/system/system.h" #include "../basic/config.h" +#include "../../util/util.h" using std::cout; using std::endl; @@ -56,7 +58,7 @@ static void load_seq_data(CBioseq& bioseq, CBioseq_Handle bioseq_handle, _it it) if (v.GetCoding() != CSeq_data::e_Iupacaa) throw std::runtime_error("Invalid sequence coding in BLAST database."); - for (size_t i = 0; i < v.size(); ++i) { + for (ncbi::TSeqPos i = 0; i < v.size(); ++i) { const auto l = v[i] & 31; const Letter s = IUPACAA_TO_STD[l]; if (s == -1) @@ -84,8 +86,8 @@ list>::const_iterator best_id(const list>& ids) { return min; } -BlastDB::BlastDB(const std::string& file_name, Metadata metadata, Flags flags) : - SequenceFile(Type::BLAST, Alphabet::NCBI, flags), +BlastDB::BlastDB(const std::string& file_name, Metadata metadata, Flags flags, const ValueTraits& value_traits) : + SequenceFile(Type::BLAST, Alphabet::NCBI, flags, FormatFlags::TITLES_LAZY | FormatFlags::SEEKABLE | FormatFlags::LENGTH_LOOKUP, value_traits), file_name_(file_name), db_(new CSeqDBExpert(file_name, CSeqDB::eProtein)), oid_(0), @@ -101,6 +103,22 @@ BlastDB::BlastDB(const std::string& file_name, Metadata metadata, Flags flags) : throw std::runtime_error("Accession file not found. BLAST databases require preprocessing using this command line: diamond prepdb -d DATABASE_PATH"); if (config.multiprocessing) throw std::runtime_error("Multiprocessing mode is not compatible with BLAST databases."); + + if (flag_any(metadata, Metadata::TAXON_NODES)) { + const string path = extract_dir(SeqDB_ResolveDbPathNoExtension(db_->GetDBNameList(), 'p')); + if (path.empty()) + throw std::runtime_error("Could not find BLAST database path."); + try { + taxon_nodes_.reset(new TaxonomyNodes(path + dir_separator + "nodes.dmp", true)); + } + catch (FileOpenException&) { + throw std::runtime_error("Taxonomy nodes file (nodes.dmp) was not found in the BLAST database directory."); + } + } +} + +int64_t BlastDB::file_count() const { + return 1; } void BlastDB::init_seqinfo_access() @@ -114,14 +132,19 @@ void BlastDB::init_seq_access() void BlastDB::seek_chunk(const Chunk& chunk) { + throw OperationNotSupported(); } -size_t BlastDB::tell_seq() const +OId BlastDB::tell_seq() const { return oid_; } -SeqInfo BlastDB::read_seqinfo() +bool BlastDB::eof() const { + return oid_ == sequence_count(); +} + +SequenceFile::SeqInfo BlastDB::read_seqinfo() { if (oid_ >= db_->GetNumOIDs()) { ++oid_; @@ -143,7 +166,7 @@ size_t BlastDB::id_len(const SeqInfo& seq_info, const SeqInfo& seq_info_next) if(flag_any(flags_, Flags::FULL_TITLES)) return full_id(*db_->GetBioseq((BlastOid)seq_info.pos), nullptr, long_seqids_, true).length(); else { - return (*best_id(db_->GetSeqIDs(seq_info.pos)))->GetSeqIdString(true).length(); + return (*best_id(db_->GetSeqIDs((BlastOid)seq_info.pos)))->GetSeqIdString(true).length(); } } @@ -165,11 +188,11 @@ void BlastDB::read_seq_data(Letter* dst, size_t len, size_t& pos, bool seek) void BlastDB::read_id_data(const int64_t oid, char* dst, size_t len) { if (flag_any(flags_, Flags::FULL_TITLES)) { - const string id = full_id(*db_->GetBioseq(oid), nullptr, long_seqids_, true); + const string id = full_id(*db_->GetBioseq((BlastOid)oid), nullptr, long_seqids_, true); std::copy(id.begin(), id.begin() + len, dst); } else { - const string id = (*best_id(db_->GetSeqIDs(oid)))->GetSeqIdString(true); + const string id = (*best_id(db_->GetSeqIDs((BlastOid)oid)))->GetSeqIdString(true); std::copy(id.begin(), id.end(), dst); } dst[len] = '\0'; @@ -180,10 +203,10 @@ void BlastDB::skip_id_data() //++oid_seqdata_; } -std::string BlastDB::seqid(size_t oid) const +std::string BlastDB::seqid(OId oid) const { if (flag_any(flags_, Flags::FULL_TITLES)) { - return full_id(*db_->GetBioseq(oid), nullptr, long_seqids_, true); + return full_id(*db_->GetBioseq((int)oid), nullptr, long_seqids_, true); } else { if (oid >= acc_.size()) @@ -192,24 +215,24 @@ std::string BlastDB::seqid(size_t oid) const } } -std::string BlastDB::dict_title(size_t dict_id, const size_t ref_block) const +std::string BlastDB::dict_title(DictId dict_id, const size_t ref_block) const { - if (dict_id >= dict_oid_[dict_block(ref_block)].size()) + if (dict_id >= (DictId)dict_oid_[dict_block(ref_block)].size()) throw std::runtime_error("Dictionary not loaded."); return seqid(dict_oid_[dict_block(ref_block)][dict_id]); } -size_t BlastDB::dict_len(size_t dict_id, const size_t ref_block) const +Loc BlastDB::dict_len(DictId dict_id, const size_t ref_block) const { - if (dict_id >= dict_oid_[dict_block(ref_block)].size()) + if (dict_id >= (int64_t)dict_oid_[dict_block(ref_block)].size()) throw std::runtime_error("Dictionary not loaded."); - return db_->GetSeqLength(dict_oid_[dict_block(ref_block)][dict_id]); + return db_->GetSeqLength((int)dict_oid_[dict_block(ref_block)][dict_id]); } -std::vector BlastDB::dict_seq(size_t dict_id, const size_t ref_block) const +std::vector BlastDB::dict_seq(DictId dict_id, const size_t ref_block) const { const size_t b = dict_block(ref_block); - if (dict_id >= dict_oid_[b].size()) + if (dict_id >= (DictId)dict_oid_[b].size()) throw std::runtime_error("Dictionary not loaded."); vector v; seq_data(dict_oid_[b][dict_id], v); @@ -217,12 +240,12 @@ std::vector BlastDB::dict_seq(size_t dict_id, const size_t ref_block) co return v; } -size_t BlastDB::sequence_count() const +int64_t BlastDB::sequence_count() const { return db_->GetNumOIDs(); } -size_t BlastDB::sparse_sequence_count() const +int64_t BlastDB::sparse_sequence_count() const { return db_->GetNumSeqs(); } @@ -242,7 +265,7 @@ int BlastDB::program_build_version() const return 0; } -void BlastDB::read_seq(std::vector& seq, std::string& id) +bool BlastDB::read_seq(std::vector& seq, std::string& id, std::vector* quals) { id.clear(); CRef bioseq = db_->GetBioseq(oid_); @@ -255,6 +278,7 @@ void BlastDB::read_seq(std::vector& seq, std::string& id) load_seq_data(*bioseq, bioseq_handle, std::back_inserter(seq)); ++oid_; + return true; } SequenceFile::Metadata BlastDB::metadata() const @@ -262,16 +286,6 @@ SequenceFile::Metadata BlastDB::metadata() const return Metadata(); } -TaxonomyNodes* BlastDB::taxon_nodes() -{ - return nullptr; -} - -std::vector* BlastDB::taxon_scientific_names() -{ - return nullptr; -} - int BlastDB::build_version() { return 0; @@ -279,18 +293,20 @@ int BlastDB::build_version() void BlastDB::create_partition_balanced(size_t max_letters) { + throw OperationNotSupported(); } void BlastDB::save_partition(const std::string& partition_file_name, const std::string& annotation) { + throw OperationNotSupported(); } -size_t BlastDB::get_n_partition_chunks() +int BlastDB::get_n_partition_chunks() { - return size_t(); + throw OperationNotSupported(); } -void BlastDB::set_seqinfo_ptr(size_t i) +void BlastDB::set_seqinfo_ptr(int64_t i) { oid_ = (int)i; } @@ -341,25 +357,30 @@ BitVector* BlastDB::filter_by_accession(const std::string& file_name) return v; } -BitVector* BlastDB::filter_by_taxonomy(const std::string& include, const std::string& exclude, TaxonomyNodes& nodes) -{ - return nullptr; -} - std::string BlastDB::file_name() { return file_name_; } -std::vector BlastDB::taxids(size_t oid) const +std::vector BlastDB::taxids(size_t oid) const { - return std::vector(); + vector v; + db_->GetTaxIDs((BlastOid)oid, v); + return v; +} + +string BlastDB::taxon_scientific_name(TaxId taxid) const { + SSeqDBTaxInfo info; + if(CSeqDBTaxInfo::GetTaxNames(taxid, info)) + return info.scientific_name; + else + return std::to_string(taxid); } void BlastDB::seq_data(size_t oid, std::vector& dst) const { const char* buf; - const int db_len = db_->GetSequence(oid, &buf); + const int db_len = db_->GetSequence((int)oid, &buf); dst.clear(); dst.resize(db_len); std::copy(buf, buf + db_len, dst.data()); @@ -368,7 +389,7 @@ void BlastDB::seq_data(size_t oid, std::vector& dst) const size_t BlastDB::seq_length(size_t oid) const { - return db_->GetSeqLength(oid); + return db_->GetSeqLength((int)oid); } const char* BlastDB::ACCESSION_FIELD = "#accession*"; @@ -412,16 +433,13 @@ void BlastDB::end_random_access(bool dictionary) free_dictionary(); } -std::vector BlastDB::accession_to_oid(const std::string& acc) const +std::vector BlastDB::accession_to_oid(const std::string& acc) const { - vector out; - db_->AccessionToOids(acc, out); - return out; -} - -SequenceFile::LoadTitles BlastDB::load_titles() -{ - return LoadTitles::LAZY; + vector r; + db_->AccessionToOids(acc, r); + if(r.empty()) + throw runtime_error("Accession not found in database: " + acc); + return vector(r.begin(), r.end()); } const BitVector* BlastDB::builtin_filter() { @@ -440,41 +458,12 @@ const BitVector* BlastDB::builtin_filter() { BlastDB::~BlastDB() { + close(); } -void BlastDB::write_dict_entry(size_t block, size_t oid, size_t len, const char* id, const Letter* seq, const double self_aln_score) -{ - *dict_file_ << (uint32_t)oid; - if (flag_any(flags_, Flags::SELF_ALN_SCORES)) - *dict_file_ << self_aln_score; -} - -bool BlastDB::load_dict_entry(InputFile& f, const size_t ref_block) -{ - uint32_t oid; - try { - f >> oid; - } - catch (EndOfStream&) { - return false; - } - const int64_t b = dict_block(ref_block); - dict_oid_[b].push_back(oid); - if (flag_any(flags_, Flags::SELF_ALN_SCORES)) { - double self_aln_score; - f >> self_aln_score; - dict_self_aln_score_[b].push_back(self_aln_score); - } - return true; -} - -void BlastDB::reserve_dict(const size_t ref_blocks) -{ -} - -void prep_blast_db() { +void BlastDB::prep_blast_db(const string& path) { vector paths; - CSeqDB::FindVolumePaths(config.database, CSeqDB::eProtein, paths); + CSeqDB::FindVolumePaths(path, CSeqDB::eProtein, paths); for (const string& db : paths) { message_stream << "Processing volume: " << db << endl; CSeqDB volume(db, CSeqDB::eProtein); @@ -504,3 +493,11 @@ void prep_blast_db() { out.close(); } } + +void BlastDB::init_write() { + throw OperationNotSupported(); +} + +void BlastDB::write_seq(const Sequence& seq, const std::string& id) { + throw OperationNotSupported(); +} diff --git a/src/data/blastdb/blastdb.h b/src/data/blastdb/blastdb.h index b3fac3a66..dfa00ca11 100644 --- a/src/data/blastdb/blastdb.h +++ b/src/data/blastdb/blastdb.h @@ -8,12 +8,14 @@ using BlastOid = int; struct BlastDB : public SequenceFile { - BlastDB(const std::string& file_name, Metadata metadata, Flags flags); + BlastDB(const std::string& file_name, Metadata metadata, Flags flags, const ValueTraits& value_traits = amino_acid_traits); + virtual int64_t file_count() const override; virtual void init_seqinfo_access() override; virtual void init_seq_access() override; virtual void seek_chunk(const Chunk& chunk) override; - virtual size_t tell_seq() const override; + virtual OId tell_seq() const override; + virtual bool eof() const override; virtual SeqInfo read_seqinfo() override; virtual void putback_seqinfo() override; virtual size_t id_len(const SeqInfo& seq_info, const SeqInfo& seq_info_next) override; @@ -21,57 +23,53 @@ struct BlastDB : public SequenceFile { virtual void read_seq_data(Letter* dst, size_t len, size_t& pos, bool seek) override; virtual void read_id_data(const int64_t oid, char* dst, size_t len) override; virtual void skip_id_data() override; - virtual std::string seqid(size_t oid) const override; - virtual std::string dict_title(size_t dict_id, const size_t ref_block) const override; - virtual size_t dict_len(size_t dict_id, const size_t ref_block) const override; - virtual std::vector dict_seq(size_t dict_id, const size_t ref_block) const override; - virtual size_t sequence_count() const override; - virtual size_t sparse_sequence_count() const override; + virtual std::string seqid(OId oid) const override; + virtual std::string dict_title(DictId dict_id, const size_t ref_block) const override; + virtual Loc dict_len(DictId dict_id, const size_t ref_block) const override; + virtual std::vector dict_seq(DictId dict_id, const size_t ref_block) const override; + virtual int64_t sequence_count() const override; + virtual int64_t sparse_sequence_count() const override; virtual size_t letters() const override; virtual int db_version() const override; virtual int program_build_version() const override; - virtual void read_seq(std::vector& seq, std::string& id) override; + virtual bool read_seq(std::vector& seq, std::string& id, std::vector* quals = nullptr) override; virtual Metadata metadata() const override; - virtual TaxonomyNodes* taxon_nodes() override; - virtual std::vector* taxon_scientific_names() override; + virtual std::string taxon_scientific_name(TaxId taxid) const override; virtual int build_version() override; virtual void create_partition_balanced(size_t max_letters) override; virtual void save_partition(const std::string& partition_file_name, const std::string& annotation = "") override; - virtual size_t get_n_partition_chunks() override; - virtual void set_seqinfo_ptr(size_t i) override; + virtual int get_n_partition_chunks() override; + virtual void set_seqinfo_ptr(OId i) override; virtual void close() override; virtual void close_weakly() override; virtual void reopen() override; virtual BitVector* filter_by_accession(const std::string& file_name) override; - virtual BitVector* filter_by_taxonomy(const std::string& include, const std::string& exclude, TaxonomyNodes& nodes) override; virtual const BitVector* builtin_filter() override; virtual std::string file_name() override; - virtual std::vector taxids(size_t oid) const override; + virtual std::vector taxids(size_t oid) const override; virtual void seq_data(size_t oid, std::vector& dst) const override; virtual size_t seq_length(size_t oid) const override; virtual void init_random_access(const size_t query_block, const size_t ref_blocks, bool dictionary = true) override; virtual void end_random_access(bool dictionary = true) override; - virtual std::vector accession_to_oid(const std::string& acc) const override; - virtual LoadTitles load_titles() override; + virtual std::vector accession_to_oid(const std::string& acc) const override; + virtual void init_write() override; + virtual void write_seq(const Sequence& seq, const std::string& id) override; virtual ~BlastDB(); + static void prep_blast_db(const std::string& path); + static const char* ACCESSION_FIELD; private: - virtual void write_dict_entry(size_t block, size_t oid, size_t len, const char* id, const Letter* seq, const double self_aln_score) override; - virtual bool load_dict_entry(InputFile& f, const size_t ref_block) override; - virtual void reserve_dict(const size_t ref_blocks) override; - const std::string file_name_; std::unique_ptr db_; int oid_; const bool long_seqids_; const Flags flags_; BitVector oid_filter_; - StringSet acc_; friend void load_blast_seqid(); friend void load_blast_seqid_lin(); -}; \ No newline at end of file +}; diff --git a/src/data/block.cpp b/src/data/block/block.cpp similarity index 52% rename from src/data/block.cpp rename to src/data/block/block.cpp index 3d4959268..6a30e8b7e 100644 --- a/src/data/block.cpp +++ b/src/data/block/block.cpp @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2022 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -23,30 +23,27 @@ along with this program. If not, see . #include #include #include -#include "sequence_set.h" -#include "../basic/translate.h" +#include +#include +#include "../sequence_set.h" #include "../util/seq_file_format.h" #include "block.h" #include "../util/sequence/sequence.h" -#include "sequence_file.h" +#include "../sequence_file.h" #include "../basic/config.h" #include "../dp/ungapped.h" +#define _REENTRANT +#include "../../lib/ips4o/ips4o.hpp" +using std::vector; using std::mutex; using std::lock_guard; using std::array; - -static bool looks_like_dna(const Sequence& seq) { - array count; - count.fill(0); - for (Loc i = 0; i < seq.length(); ++i) - ++count[(int)seq[i]]; - return count[(int)value_traits.from_char('A')] - + count[(int)value_traits.from_char('C')] - + count[(int)value_traits.from_char('G')] - + count[(int)value_traits.from_char('T')] - + count[(int)value_traits.from_char('N')] == seq.length(); -} +using std::pair; +using std::greater; +using std::string; +using std::runtime_error; +using std::numeric_limits; Block::Block(Alphabet alphabet): seqs_(alphabet), @@ -79,88 +76,45 @@ TranslatedSequence Block::translated(size_t block_id) const } bool Block::long_offsets() const { - return seqs_.raw_len() > (size_t)std::numeric_limits::max(); + return seqs_.raw_len() > (int64_t)std::numeric_limits::max(); } -static size_t push_seq(SequenceSet& ss, SequenceSet& source_seqs, const vector& seq, unsigned frame_mask, SequenceType seq_type) -{ - if (seq_type == SequenceType::amino_acid) { - ss.push_back(seq.cbegin(), seq.cend()); - return seq.size(); +int64_t Block::push_back(const Sequence& seq, const char* id, const std::vector* quals, const OId oid, const SequenceType seq_type, const int frame_mask, const bool dna_translation) { + static const char* const OVERFLOW_ERR = "Sequences in block exceed supported maximum."; + if (block2oid_.size() == numeric_limits::max()) + throw runtime_error(OVERFLOW_ERR); + if (id) + ids_.push_back(id, id + strlen(id)); + if (quals) + qual_.push_back(quals->cbegin(), quals->cend()); + block2oid_.push_back(oid); + if (seq_type == SequenceType::amino_acid || !dna_translation) { + seqs_.push_back(seq.data(), seq.end()); + return seq.length(); } else { - source_seqs.push_back(seq.cbegin(), seq.cend()); - if (seq.size() < 2) { - for (unsigned j = 0; j < 6; ++j) - ss.fill(0, value_traits.mask_char); - return 0; - } - vector proteins[6]; - size_t n = Translator::translate(seq, proteins); - - unsigned bestFrames(Translator::computeGoodFrames(proteins, config.get_run_len((unsigned)seq.size() / 3))); - for (unsigned j = 0; j < 6; ++j) { - if ((bestFrames & (1 << j)) && (frame_mask & (1 << j))) - ss.push_back(proteins[j].cbegin(), proteins[j].cend()); + if (seqs_.size() > numeric_limits::max() - 6) + throw runtime_error(OVERFLOW_ERR); + source_seqs_.push_back(seq.data(), seq.end()); + auto t = Util::Seq::translate(seq); + const Loc min_len = config.min_orf_len((Loc)t.front().size()); + int64_t letters = 0; + for (int j = 0; j < 6; ++j) { + if (frame_mask & (1 << j)) { + letters += Util::Seq::find_orfs(t[j], min_len); + seqs_.push_back(t[j].cbegin(), t[j].cend()); + } else - ss.fill(proteins[j].size(), value_traits.mask_char); + seqs_.fill(t[j].size(), MASK_LETTER); } - return n; + return letters; } } -Block::Block(std::list::iterator file_begin, - std::list::iterator file_end, - const Sequence_file_format& format, - size_t max_letters, - const ValueTraits& value_traits, - bool with_quals, - bool lazy_masking, - size_t modulo): - seqs_(Alphabet::STD), - source_seqs_(Alphabet::STD), - unmasked_seqs_(Alphabet::STD), - soft_masked_(false) -{ - static constexpr size_t CHECK_FOR_DNA_COUNT = 10; - size_t letters = 0, n = 0; - vector seq; - string id; - vector qual; - string id2; - - unsigned frame_mask = (1 << 6) - 1; - if (config.query_strands == "plus") - frame_mask = (1 << 3) - 1; - else if (config.query_strands == "minus") - frame_mask = ((1 << 3) - 1) << 3; - - std::list::iterator file_it = file_begin; - bool read_success = true; - - while ((letters < max_letters || (n % modulo != 0)) && (read_success = format.get_seq(id, seq, *file_it, value_traits, with_quals ? &qual : nullptr))) { - if (seq.size() > 0) { - ids_.push_back(id.begin(), id.end()); - letters += push_seq(seqs_, source_seqs_, seq, frame_mask, value_traits.seq_type); - if (with_quals) - qual_.push_back(qual.begin(), qual.end()); - ++n; - if (seqs_.size() > (size_t)std::numeric_limits::max()) - throw std::runtime_error("Number of sequences in file exceeds supported maximum."); - if (n <= CHECK_FOR_DNA_COUNT && value_traits.seq_type == amino_acid && looks_like_dna(Sequence(seq)) && !config.ignore_warnings) - throw std::runtime_error("The sequences are expected to be proteins but only contain DNA letters. Use the option --ignore-warnings to proceed."); - } - ++file_it; - if (file_it == file_end) - file_it = file_begin; - } - ids_.finish_reserve(); - if (with_quals) - qual_.finish_reserve(); - seqs_.finish_reserve(); - source_seqs_.finish_reserve(); - if (file_it != file_begin || (!read_success && ++file_it != file_end && format.get_seq(id, seq, *file_it, value_traits, nullptr))) - throw std::runtime_error("Unequal number of sequences in paired read files."); +void Block::append(const Block& b) { + seqs_.append(b.seqs_); + ids_.append(b.ids_); + block2oid_.insert(block2oid_.end(), b.block2oid_.begin(), b.block2oid_.end()); } bool Block::fetch_seq_if_unmasked(size_t block_id, std::vector& seq) { @@ -185,7 +139,7 @@ void Block::write_masked_seq(size_t block_id, const std::vector& seq) { masked_[block_id] = true; } -uint32_t Block::dict_id(size_t block, size_t block_id, SequenceFile& db) const +DictId Block::dict_id(size_t block, BlockId block_id, SequenceFile& db) const { string t; if (has_ids()) { @@ -244,7 +198,7 @@ void Block::compute_self_aln() { } }; vector t; - for (size_t i = 0; i < config.threads_; ++i) + for (int i = 0; i < config.threads_; ++i) t.emplace_back(worker); for (auto& i : t) i.join(); @@ -253,4 +207,59 @@ void Block::compute_self_aln() { double Block::self_aln_score(const int64_t block_id) const { return self_aln_score_[block_id]; +} + +BlockId Block::oid2block_id(OId i) const { + if (block2oid_.back() - block2oid_.front() != seqs_.size() - 1) + throw std::runtime_error("Block has a sparse OId range."); + if (i < block2oid_.front() || i > block2oid_.back()) + throw std::runtime_error("OId not contained in block."); + return BlockId(i - block2oid_.front()); +} + +SeqInfo Block::seq_info(const BlockId id) const { + static const char* blank = ""; + auto mate_id = (id % 2) == 0 ? id + 1 : id - 1; + return { id, block_id2oid(id), ids_.empty() ? nullptr : ids_[id], qual_.empty() ? blank : qual_[id], + align_mode.query_translated ? source_seqs_.length(id) : seqs_.length(id), + align_mode.query_translated ? source_seqs_[id] : seqs_[id], + align_mode.query_translated && mate_id < source_seqs_.size() ? source_seqs_[mate_id] : Sequence() }; +} + +Block* Block::length_sorted(int threads) const { + const BlockId n = seqs_.size(); + vector> lengths = seqs_.lengths(); +#if _MSC_FULL_VER == 191627045 || !defined(NDEBUG) + std::sort(lengths.begin(), lengths.end(), greater>()); +#else + ips4o::parallel::sort(lengths.begin(), lengths.end(), greater>(), threads); +#endif + Block *b = new Block(); + for (BlockId i = 0; i < n; ++i) { + const BlockId j = lengths[i].second; + b->seqs_.reserve(seqs_.length(j)); + b->ids_.reserve(ids_.length(j)); + } + b->seqs_.finish_reserve(); + b->ids_.finish_reserve(); + b->block2oid_.reserve(n); + for (BlockId i = 0; i < n; ++i) { + const BlockId j = lengths[i].second; + b->seqs_.assign(i, seqs_.ptr(j), seqs_.end(j)); + b->ids_.assign(i, ids_.ptr(j), ids_.end(j)); + b->block2oid_.push_back(block2oid_.at(j)); + } + return b; +} + +bool Block::has_ids() const { + return !ids_.empty(); +} + +BlockId Block::source_seq_count() const { + return source_seqs_.empty() ? seqs_.size() : source_seqs_.size(); +} + +int64_t Block::mem_size() const { + return seqs_.mem_size() + source_seqs_.mem_size() + unmasked_seqs_.mem_size() + ids_.mem_size() + qual_.mem_size() + block2oid_.size() * sizeof(OId) + soft_masking_table_.mem_size(); } \ No newline at end of file diff --git a/src/data/block.h b/src/data/block/block.h similarity index 69% rename from src/data/block.h rename to src/data/block/block.h index 84e99e6f8..bc3ef1170 100644 --- a/src/data/block.h +++ b/src/data/block/block.h @@ -22,24 +22,24 @@ along with this program. If not, see . #include #include #include -#include "sequence_set.h" -#include "seed_histogram.h" +#include "../sequence_set.h" +#include "../seed_histogram.h" #include "../util/seq_file_format.h" #include "../masking/masking.h" struct SequenceFile; +struct SeqInfo { + BlockId block_id; + OId oid; + const char* title, *qual; + Loc len; + Sequence source_seq, mate_seq; +}; + struct Block { - Block(Alphabet alphabet); - Block(std::list::iterator file_begin, - std::list::iterator file_end, - const Sequence_file_format& format, - size_t max_letters, - const ValueTraits& value_traits, - bool with_quals, - bool lazy_masking = false, - size_t modulo = 1); + Block(Alphabet alphabet = Alphabet::STD); unsigned source_len(unsigned block_id) const; TranslatedSequence translated(size_t block_id) const; bool long_offsets() const; @@ -71,18 +71,24 @@ struct Block { SeedHistogram& hst() { return hst_; } - size_t block_id2oid(size_t i) const { + OId block_id2oid(BlockId i) const { return block2oid_[i]; } + OId oid_begin() const { + return *std::min_element(block2oid_.begin(), block2oid_.end()); + //return block2oid_.front(); + } + OId oid_end() const { + return *std::max_element(block2oid_.begin(), block2oid_.end()) + 1; + //return block2oid_.back() + 1; + } + BlockId oid2block_id(OId i) const; Alphabet alphabet() const { return seqs_.alphabet(); } - bool has_ids() const { - return !ids_.empty(); - } bool fetch_seq_if_unmasked(size_t block_id, std::vector& seq); void write_masked_seq(size_t block_id, const std::vector& seq); - uint32_t dict_id(size_t block, size_t block_id, SequenceFile& db) const; + DictId dict_id(size_t block, BlockId block_id, SequenceFile& db) const; void soft_mask(const MaskingAlgo algo); void remove_soft_masking(const int template_len, const bool add_bit_mask); bool soft_masked() const; @@ -90,8 +96,15 @@ struct Block { void compute_self_aln(); double self_aln_score(const int64_t block_id) const; bool has_self_aln() const { - return self_aln_score_.size() == seqs_.size(); + return (BlockId)self_aln_score_.size() == seqs_.size(); } + int64_t push_back(const Sequence& seq, const char* id, const std::vector* quals, const OId oid, const SequenceType seq_type, const int frame_mask, const bool dna_translation = true); + void append(const Block& b); + SeqInfo seq_info(const BlockId id) const; + Block* length_sorted(int threads) const; + bool has_ids() const; + BlockId source_seq_count() const; + int64_t mem_size() const; private: @@ -99,7 +112,7 @@ struct Block { StringSet ids_; StringSet qual_; SeedHistogram hst_; - std::vector block2oid_; + std::vector block2oid_; std::vector masked_; std::vector self_aln_score_; std::mutex mask_lock_; diff --git a/src/data/block/block_wrapper.cpp b/src/data/block/block_wrapper.cpp new file mode 100644 index 000000000..9ee5e336b --- /dev/null +++ b/src/data/block/block_wrapper.cpp @@ -0,0 +1,207 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2021 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#include "block_wrapper.h" + +using std::vector; +using std::string; + +BlockWrapper::BlockWrapper(const Block& block, Metadata metadata, Flags flags, const ValueTraits& value_traits) : + SequenceFile(SequenceFile::Type::BLOCK, Alphabet::STD, flags, FormatFlags::LENGTH_LOOKUP | FormatFlags::TITLES_LAZY | FormatFlags::SEEKABLE, value_traits), + block_(block), + oid_(0) +{ +} + +int64_t BlockWrapper::file_count() const { + return 1; +} + +bool BlockWrapper::files_synced() { + return true; +} + +SequenceFile::SeqInfo BlockWrapper::read_seqinfo() { + if (oid_ >= block_.seqs().size()) { + ++oid_; + return SeqInfo(0, 0); + } + const Loc l = block_.seqs().length(oid_); + if (l == 0) + throw std::runtime_error("Database with sequence length 0 is not supported"); + return SeqInfo(oid_++, l); +} + +void BlockWrapper::putback_seqinfo() { + --oid_; +} + +void BlockWrapper::close() { +} + +void BlockWrapper::set_seqinfo_ptr(OId i) { + oid_ = i; +} + +OId BlockWrapper::tell_seq() const { + return oid_; +} + +bool BlockWrapper::eof() const { + return oid_ >= block_.seqs().size(); +} + +void BlockWrapper::init_seq_access() { + set_seqinfo_ptr(0); +} + +bool BlockWrapper::read_seq(vector& seq, string& id, std::vector* quals) +{ + throw OperationNotSupported(); +} + +void BlockWrapper::create_partition_balanced(size_t max_letters) { + throw OperationNotSupported(); +} + +void BlockWrapper::save_partition(const string& partition_file_name, const string& annotation) { + throw OperationNotSupported(); +} + +int BlockWrapper::get_n_partition_chunks() { + throw OperationNotSupported(); +} + +void BlockWrapper::init_seqinfo_access() { +} + +void BlockWrapper::seek_chunk(const Chunk& chunk) { + throw OperationNotSupported(); +} + +std::string BlockWrapper::seqid(OId oid) const { + return block_.ids()[oid]; +} + +size_t BlockWrapper::id_len(const SeqInfo& seq_info, const SeqInfo& seq_info_next) { + return block_.ids().length(seq_info.pos); +} + +void BlockWrapper::seek_offset(size_t p) { +} + +void BlockWrapper::read_seq_data(Letter* dst, size_t len, size_t& pos, bool seek) { + *(dst - 1) = Sequence::DELIMITER; + *(dst + len) = Sequence::DELIMITER; + std::copy(block_.seqs().ptr(pos), block_.seqs().end(pos), dst); + ++pos; +} + +void BlockWrapper::read_id_data(const int64_t oid, char* dst, size_t len) { + std::copy(block_.ids().ptr(oid), block_.ids().end(oid), dst); + dst[len] = '\0'; +} + +void BlockWrapper::skip_id_data() { +} + +int64_t BlockWrapper::sequence_count() const { + return block_.seqs().size(); +} + +size_t BlockWrapper::letters() const { + return block_.seqs().letters(); +} + +int BlockWrapper::db_version() const { + throw OperationNotSupported(); +} + +int BlockWrapper::program_build_version() const { + throw OperationNotSupported(); +} + +SequenceFile::Metadata BlockWrapper::metadata() const { + return Metadata(); +} + +int BlockWrapper::build_version() { + throw OperationNotSupported(); +} + +BlockWrapper::~BlockWrapper() +{ +} + +void BlockWrapper::close_weakly() +{ +} + +void BlockWrapper::reopen() +{ +} + +BitVector* BlockWrapper::filter_by_accession(const std::string& file_name) +{ + throw OperationNotSupported(); +} + +const BitVector* BlockWrapper::builtin_filter() +{ + return nullptr; +} + +std::string BlockWrapper::file_name() +{ + return {}; +} + +int64_t BlockWrapper::sparse_sequence_count() const +{ + throw OperationNotSupported(); +} + +std::vector BlockWrapper::taxids(size_t oid) const +{ + throw OperationNotSupported(); +} + +void BlockWrapper::seq_data(size_t oid, std::vector& dst) const +{ + throw OperationNotSupported(); +} + +size_t BlockWrapper::seq_length(size_t oid) const +{ + throw OperationNotSupported(); +} + +void BlockWrapper::init_random_access(const size_t query_block, const size_t ref_blocks, bool dictionary) +{ + if (dictionary) + load_dictionary(query_block, ref_blocks); +} + +void BlockWrapper::end_random_access(bool dictionary) +{ + if (!dictionary) + return; + free_dictionary(); +} diff --git a/src/data/block/block_wrapper.h b/src/data/block/block_wrapper.h new file mode 100644 index 000000000..8f36e9701 --- /dev/null +++ b/src/data/block/block_wrapper.h @@ -0,0 +1,55 @@ +#pragma once +#include "../sequence_file.h" +#include "block.h" + +struct BlockWrapper : public SequenceFile +{ + + BlockWrapper(const Block& block, Metadata metadata = Metadata(), Flags flags = Flags::NONE, const ValueTraits& value_traits = amino_acid_traits); + + virtual int64_t file_count() const override; + virtual void create_partition_balanced(size_t max_letters) override; + virtual void save_partition(const std::string& partition_file_name, const std::string& annotation = "") override; + virtual int get_n_partition_chunks() override; + virtual void close() override; + virtual void set_seqinfo_ptr(OId i) override; + virtual OId tell_seq() const override; + virtual bool eof() const override; + virtual bool files_synced() override; + virtual void init_seq_access() override; + virtual void init_seqinfo_access() override; + virtual void seek_chunk(const Chunk& chunk) override; + virtual SeqInfo read_seqinfo() override; + virtual void putback_seqinfo() override; + virtual size_t id_len(const SeqInfo& seq_info, const SeqInfo& seq_info_next) override; + virtual void seek_offset(size_t p) override; + virtual void read_seq_data(Letter* dst, size_t len, size_t& pos, bool seek) override; + virtual void read_id_data(const int64_t oid, char* dst, size_t len) override; + virtual void skip_id_data() override; + virtual std::string seqid(OId oid) const override; + virtual int64_t sequence_count() const override; + virtual bool read_seq(std::vector& seq, std::string& id, std::vector* quals = nullptr) override; + virtual size_t letters() const override; + virtual int db_version() const override; + virtual int program_build_version() const override; + virtual Metadata metadata() const override; + virtual int build_version() override; + virtual ~BlockWrapper(); + virtual void close_weakly() override; + virtual void reopen() override; + virtual BitVector* filter_by_accession(const std::string& file_name) override; + virtual const BitVector* builtin_filter() override; + virtual std::string file_name() override; + virtual int64_t sparse_sequence_count() const override; + virtual std::vector taxids(size_t oid) const override; + virtual void seq_data(size_t oid, std::vector& dst) const override; + virtual size_t seq_length(size_t oid) const override; + virtual void init_random_access(const size_t query_block, const size_t ref_blocks, bool dictionary = true) override; + virtual void end_random_access(bool dictionary = true) override; + +private: + + const Block& block_; + OId oid_; + +}; diff --git a/src/data/dmnd/dmnd.cpp b/src/data/dmnd/dmnd.cpp index 89cea890b..7e2ad9bd3 100644 --- a/src/data/dmnd/dmnd.cpp +++ b/src/data/dmnd/dmnd.cpp @@ -34,19 +34,25 @@ along with this program. If not, see . #include "../util/parallel/multiprocessing.h" #include "dmnd.h" #include "../reference.h" -#include "../load_seqs.h" #include "../taxonomy.h" #include "../util/system/system.h" #include "../util/algo/external_sort.h" #include "../../util/util.h" +#include "../fasta/fasta_file.h" +#include "../../util/sequence/sequence.h" using std::tuple; using std::string; using std::list; using std::cout; using std::endl; +using std::unique_ptr; +using std::pair; +using std::runtime_error; const char* DatabaseFile::FILE_EXTENSION = ".dmnd"; +const uint32_t ReferenceHeader::current_db_version_prot = 3; +const uint32_t ReferenceHeader::current_db_version_nucl = 4; Serializer& operator<<(Serializer &s, const ReferenceHeader2 &h) { @@ -54,17 +60,27 @@ Serializer& operator<<(Serializer &s, const ReferenceHeader2 &h) s << sizeof(ReferenceHeader2); s.write(h.hash, sizeof(h.hash)); s << h.taxon_array_offset << h.taxon_array_size << h.taxon_nodes_offset << h.taxon_names_offset; +#ifdef EXTRA + s << (int32_t)h.db_type; +#endif return s; } Deserializer& operator>>(Deserializer &d, ReferenceHeader2 &h) { + int32_t db_type; d.read_record().read(h.hash, sizeof(h.hash)) >> h.taxon_array_offset >> h.taxon_array_size >> h.taxon_nodes_offset >> h.taxon_names_offset +#ifdef EXTRA + >> db_type +#endif >> Finish(); +#ifdef EXTRA + h.db_type = (SequenceType)db_type; +#endif return d; } @@ -82,18 +98,18 @@ Serializer& operator<<(Serializer& file, const ReferenceHeader& h) return file; } -InputFile& operator>>(InputFile& file, SeqInfo& r) { +InputFile& operator>>(InputFile& file, SequenceFile::SeqInfo& r) { uint32_t p; file >> r.pos >> r.seq_len >> p; return file; } -Serializer& operator<<(Serializer& file, const SeqInfo& r) { +Serializer& operator<<(Serializer& file, const SequenceFile::SeqInfo& r) { file << r.pos << r.seq_len << (uint32_t)0; return file; } -SeqInfo DatabaseFile::read_seqinfo() { +SequenceFile::SeqInfo DatabaseFile::read_seqinfo() { SeqInfo r; (*this) >> r; pos_array_offset += SeqInfo::SIZE; @@ -104,67 +120,6 @@ void DatabaseFile::putback_seqinfo() { pos_array_offset -= SeqInfo::SIZE; } -void DatabaseFile::write_dict_entry(size_t block, size_t oid, size_t len, const char* id, const Letter* seq, const double self_aln_score) -{ - OutputFile& f = *dict_file_; - f << (uint32_t)oid; - f << (uint32_t)len; - f << id; - if (flag_any(flags_, Flags::TARGET_SEQS)) - f.write(seq, len); - if (flag_any(flags_, Flags::SELF_ALN_SCORES)) - f << self_aln_score; - dict_alloc_size_ += strlen(id); -} - -bool DatabaseFile::load_dict_entry(InputFile& f, size_t ref_block) -{ - uint32_t oid, len; - string title; - try { - f >> oid; - } - catch (EndOfStream&) { - return false; - } - f >> len >> title; - const size_t b = dict_block(ref_block); - dict_oid_[b].push_back(oid); - dict_len_[b].push_back(len); - dict_title_[b].push_back(title.begin(), title.end()); - if (flag_any(flags_, Flags::TARGET_SEQS)) { - vector v(len); - f.read(v.data(), len); - dict_seq_[b].push_back(v.begin(), v.end()); - } - if (flag_any(flags_, Flags::SELF_ALN_SCORES)) { - double self_aln_score; - f >> self_aln_score; - dict_self_aln_score_[b].push_back(self_aln_score); - } - return true; -} - -void DatabaseFile::reserve_dict(const size_t ref_blocks) -{ - if (config.multiprocessing) { - dict_len_ = std::vector>(ref_blocks); - dict_title_ = std::vector(ref_blocks); - if (flag_any(flags_, Flags::TARGET_SEQS)) - dict_seq_ = std::vector(ref_blocks); - } - else { - dict_len_ = { {} }; - dict_len_[0].reserve(next_dict_id_); - dict_title_ = { {} }; - dict_title_[0].reserve(next_dict_id_, dict_alloc_size_); - if (flag_any(flags_, Flags::TARGET_SEQS)) { - dict_seq_ = { {} }; - dict_seq_[0].reserve(next_dict_id_, 0); - } - } -} - void DatabaseFile::init(Flags flags) { read_header(*this, ref_header); @@ -172,7 +127,7 @@ void DatabaseFile::init(Flags flags) return; if (ref_header.build < min_build_required || ref_header.db_version < MIN_DB_VERSION) throw std::runtime_error("Database was built with an older version of Diamond and is incompatible."); - if (ref_header.db_version > ReferenceHeader::current_db_version) + if (ref_header.db_version > std::max(ReferenceHeader::current_db_version_prot , ReferenceHeader::current_db_version_nucl)) throw std::runtime_error("Database was built with a newer version of Diamond and is incompatible."); if (ref_header.sequences == 0) throw std::runtime_error("Incomplete database file. Database building did not complete successfully."); @@ -180,8 +135,8 @@ void DatabaseFile::init(Flags flags) pos_array_offset = ref_header.pos_array_offset; } -DatabaseFile::DatabaseFile(const string &input_file, Metadata metadata, Flags flags): - SequenceFile(SequenceFile::Type::DMND, Alphabet::STD, flags), +DatabaseFile::DatabaseFile(const string &input_file, Metadata metadata, Flags flags, const ValueTraits& value_traits): + SequenceFile(SequenceFile::Type::DMND, Alphabet::STD, flags, FormatFlags::DICT_LENGTHS | FormatFlags::DICT_SEQIDS | FormatFlags::SEEKABLE | FormatFlags::LENGTH_LOOKUP, value_traits), InputFile(auto_append_extension_if_exists(input_file, FILE_EXTENSION), InputFile::BUFFERED), temporary(false) { @@ -202,16 +157,31 @@ DatabaseFile::DatabaseFile(const string &input_file, Metadata metadata, Flags fl if (flag_any(metadata, Metadata::TAXON_MAPPING)) taxon_list_.reset(new TaxonList(seek(header2.taxon_array_offset), ref_header.sequences, header2.taxon_array_size)); + + if (flag_any(metadata, Metadata::TAXON_SCIENTIFIC_NAMES)) { + seek(header2.taxon_names_offset); + (*this) >> taxon_scientific_names_; + } + + if (flag_any(metadata, Metadata::TAXON_NODES)) + taxon_nodes_.reset(new TaxonomyNodes(seek(header2.taxon_nodes_offset), ref_header.build)); + + if (flag_any(flags, Flags::ACC_TO_OID_MAPPING | Flags::OID_TO_ACC_MAPPING)) + read_seqid_list(); } -DatabaseFile::DatabaseFile(TempFile &tmp_file): - SequenceFile(SequenceFile::Type::DMND, Alphabet::STD, Flags::NONE), +DatabaseFile::DatabaseFile(TempFile &tmp_file, const ValueTraits& value_traits): + SequenceFile(SequenceFile::Type::DMND, Alphabet::STD, Flags::NONE, FormatFlags::DICT_LENGTHS | FormatFlags::DICT_SEQIDS | FormatFlags::SEEKABLE | FormatFlags::LENGTH_LOOKUP, value_traits), InputFile(tmp_file, 0), temporary(true) { init(); } +int64_t DatabaseFile::file_count() const { + return 1; +} + void DatabaseFile::close() { if (temporary) InputFile::close_and_delete(); @@ -240,7 +210,7 @@ bool DatabaseFile::has_taxon_scientific_names() const { return header2.taxon_names_offset != 0; } -static void push_seq(const Sequence &seq, const char *id, size_t id_len, uint64_t &offset, vector &pos_array, OutputFile &out, size_t &letters, size_t &n_seqs) +static void push_seq(const Sequence &seq, const char *id, size_t id_len, uint64_t &offset, vector &pos_array, OutputFile &out, size_t &letters, size_t &n_seqs) { pos_array.emplace_back(offset, seq.length()); out.write("\xff", 1); @@ -252,60 +222,63 @@ static void push_seq(const Sequence &seq, const char *id, size_t id_len, uint64_ offset += seq.length() + id_len + 3; } -void DatabaseFile::make_db(TempFile **tmp_out, list *input_file) +void DatabaseFile::make_db() { config.file_buffer_size = 4 * MEGABYTES; if (config.input_ref_file.size() > 1) throw std::runtime_error("Too many arguments provided for option --in."); const string input_file_name = config.input_ref_file.empty() ? string() : config.input_ref_file.front(); - if (input_file_name.empty() && !input_file) - std::cerr << "Input file parameter (--in) is missing. Input will be read from stdin." << endl; - if(!input_file && !input_file_name.empty()) + if (input_file_name.empty()) + message_stream << "Input file parameter (--in) is missing. Input will be read from stdin." << endl; + else message_stream << "Database input file: " << input_file_name << endl; task_timer total; task_timer timer("Opening the database file", true); - list* db_file; - if (input_file) - db_file = input_file; - else { - db_file = new list; - db_file->emplace_back(input_file_name); - } - OutputFile *out = tmp_out ? new TempFile() : new OutputFile(config.database); + value_traits = (config.dbtype == SequenceType::amino_acid) ? amino_acid_traits : nucleotide_traits; + FastaFile db_file({ input_file_name }, Metadata (), Flags::NONE, value_traits); + + unique_ptr out(new OutputFile(config.database)); ReferenceHeader header; - ReferenceHeader2 header2; + ReferenceHeader2 header2; - *out << header; + *out << header; *out << header2; size_t letters = 0, n = 0, n_seqs = 0, total_seqs = 0; uint64_t offset = out->tell(); - Block* block; + LoadFlags flags = SequenceFile::LoadFlags::ALL; + if (config.dbtype == SequenceType::nucleotide){ + header.db_version = ReferenceHeader::current_db_version_nucl; + flags |= SequenceFile::LoadFlags::DNA_PRESERVATION; + } + + Block* block; const FASTA_format format; vector pos_array; - ExternalSorter> accessions; - + ExternalSorter> accessions; try { while (true) { timer.go("Loading sequences"); - block = new Block(db_file->begin(), db_file->end(), format, (size_t)(1e9), amino_acid_traits, false); + block = db_file.load_seqs((int64_t)1e9, nullptr, flags); if (block->empty()) { delete block; break; } n = block->seqs().size(); - timer.go("Masking sequences"); - mask_seqs(block->seqs(), Masking::get(), false, MaskingAlgo::SEG); + if (config.dbtype == SequenceType::amino_acid && config.masking_ != "0") { + timer.go("Masking sequences"); + mask_seqs(block->seqs(), Masking::get(), false, MaskingAlgo::SEG); + } - timer.go("Writing sequences"); + timer.go("Writing sequences"); for (size_t i = 0; i < n; ++i) { Sequence seq = block->seqs()[i]; if (seq.length() == 0) - throw std::runtime_error("File format error: sequence of length 0 at line " + std::to_string(db_file->front().line_count)); + throw std::runtime_error("File format error: sequence of length 0 at line " + std::to_string(db_file.line_count())); push_seq(seq, block->ids()[i], block->ids().length(i), offset, pos_array, *out, letters, n_seqs); } if (!config.prot_accession2taxid.empty()) { @@ -343,7 +316,7 @@ void DatabaseFile::make_db(TempFile **tmp_out, list *input_file) pos_array.shrink_to_fit(); timer.finish(); - Table stats; + Util::Table stats; stats("Database sequences", n_seqs); stats("Database letters", letters); taxonomy.init(); @@ -353,19 +326,21 @@ void DatabaseFile::make_db(TempFile **tmp_out, list *input_file) header2.taxon_array_size = out->tell() - header2.taxon_array_offset; } if (!config.nodesdmp.empty()) { + TaxonomyNodes nodes(config.nodesdmp); header2.taxon_nodes_offset = out->tell(); - TaxonomyNodes::build(*out); + nodes.save(*out); } if (!config.namesdmp.empty()) { header2.taxon_names_offset = out->tell(); *out << taxonomy.name_; } - if (!input_file) { - timer.go("Closing the input file"); - db_file->front().close(); - delete db_file; - } +#ifdef EXTRA + header2.db_type = config.dbtype; +#endif + + timer.go("Closing the input file"); + db_file.close(); timer.go("Closing the database file"); header.letters = letters; @@ -373,12 +348,7 @@ void DatabaseFile::make_db(TempFile **tmp_out, list *input_file) out->seek(0); *out << header; *out << header2; - if (tmp_out) { - *tmp_out = static_cast(out); - } else { - out->close(); - delete out; - } + out->close(); timer.finish(); stats("Database hash", hex_print(header2.hash, 16)); @@ -387,19 +357,23 @@ void DatabaseFile::make_db(TempFile **tmp_out, list *input_file) message_stream << endl << stats; } -void DatabaseFile::set_seqinfo_ptr(size_t i) { +void DatabaseFile::set_seqinfo_ptr(OId i) { pos_array_offset = ref_header.pos_array_offset + SeqInfo::SIZE * i; } -size_t DatabaseFile::tell_seq() const { +OId DatabaseFile::tell_seq() const { return (pos_array_offset - ref_header.pos_array_offset) / SeqInfo::SIZE; } +bool DatabaseFile::eof() const { + return tell_seq() == sequence_count(); +} + void DatabaseFile::init_seq_access() { seek(sizeof(ReferenceHeader) + sizeof(ReferenceHeader2) + 8); } -void DatabaseFile::read_seq(vector& seq, string &id) +bool DatabaseFile::read_seq(vector& seq, string &id, std::vector* quals) { char c; read(&c, 1); @@ -407,6 +381,7 @@ void DatabaseFile::read_seq(vector& seq, string &id) id.clear(); read_to(std::back_inserter(seq), '\xff'); read_to(std::back_inserter(id), '\0'); + return false; } void DatabaseFile::skip_seq() @@ -450,7 +425,7 @@ void DatabaseFile::create_partition_balanced(size_t max_letters) { void DatabaseFile::create_partition(size_t max_letters) { task_timer timer("Create partition of DatabaseFile"); size_t letters = 0, seqs = 0, total_seqs = 0; - size_t i_chunk = 0; + int i_chunk = 0; size_t oid = 0, oid_begin; set_seqinfo_ptr(oid); @@ -485,8 +460,8 @@ void DatabaseFile::create_partition(size_t max_letters) { partition.n_seqs_total = total_seqs; } -size_t DatabaseFile::get_n_partition_chunks() { - return partition.chunks.size(); +int DatabaseFile::get_n_partition_chunks() { + return (int)partition.chunks.size(); } void DatabaseFile::save_partition(const string & partition_file_name, const string & annotation) { @@ -503,7 +478,7 @@ void DatabaseFile::save_partition(const string & partition_file_name, const stri Chunk to_chunk(const string & line) { vector tokens = split(line, ' '); - return Chunk(stoull(tokens[0]), stoull(tokens[1]), stoull(tokens[2])); + return Chunk((int)stoull(tokens[0]), stoull(tokens[1]), stoull(tokens[2])); } string to_string(const Chunk & c) { @@ -532,38 +507,9 @@ void DatabaseFile::init_seqinfo_access() { } void DatabaseFile::seek_chunk(const Chunk& chunk) { - current_ref_block = chunk.i; set_seqinfo_ptr(chunk.offset); } -std::string DatabaseFile::seqid(size_t oid) const -{ - throw std::runtime_error("Operation not supported (seqid)."); -} -std::string DatabaseFile::dict_title(size_t dict_id, const size_t ref_block) const -{ - const size_t b = dict_block(ref_block); - if (b >= dict_title_.size() || dict_id >= dict_title_[b].size()) - throw std::runtime_error("Dictionary not loaded."); - return dict_title_[b][dict_id]; -} -size_t DatabaseFile::dict_len(size_t dict_id, const size_t ref_block) const -{ - const size_t b = dict_block(ref_block); - if (b >= dict_len_.size() || dict_id >= dict_len_[b].size()) - throw std::runtime_error("Dictionary not loaded."); - return dict_len_[b][dict_id]; -} - -std::vector DatabaseFile::dict_seq(size_t dict_id, const size_t ref_block) const -{ - const size_t b = dict_block(ref_block); - if (b >= dict_seq_.size() || dict_id >= dict_seq_[b].size()) - throw std::runtime_error("Dictionary not loaded."); - Sequence s = dict_seq_[b][dict_id]; - return vector(s.data(), s.end()); -} - size_t DatabaseFile::id_len(const SeqInfo& seq_info, const SeqInfo& seq_info_next) { return seq_info_next.pos - seq_info.pos - seq_info.seq_len - 3; } @@ -588,7 +534,7 @@ void DatabaseFile::skip_id_data() { if (!seek_forward('\0')) throw std::runtime_error("Unexpected end of file."); } -size_t DatabaseFile::sequence_count() const { +int64_t DatabaseFile::sequence_count() const { return ref_header.sequences; } @@ -615,16 +561,13 @@ SequenceFile::Metadata DatabaseFile::metadata() const { return flags; } -TaxonomyNodes* DatabaseFile::taxon_nodes() { - return new TaxonomyNodes(seek(header2.taxon_nodes_offset), ref_header.build); -} - int DatabaseFile::build_version() { return ref_header.build; } DatabaseFile::~DatabaseFile() { + close(); } void DatabaseFile::close_weakly() @@ -641,25 +584,6 @@ BitVector* DatabaseFile::filter_by_accession(const std::string& file_name) return nullptr; } -BitVector* DatabaseFile::filter_by_taxonomy(const std::string& include, const std::string& exclude, TaxonomyNodes& nodes) -{ - if (!taxon_list_.get()) - throw std::runtime_error("Database does not contain taxonomy mapping."); - BitVector* v = new BitVector(taxon_list_->size()); - if (!include.empty() && !exclude.empty()) - throw std::runtime_error("Options --taxonlist and --taxon-exclude are mutually exclusive."); - const bool e = !exclude.empty(); - const std::set taxon_filter_list(parse_csv(e ? exclude : include)); - if (taxon_filter_list.empty()) - throw std::runtime_error("Option --taxonlist/--taxon-exclude used with empty list."); - if (taxon_filter_list.find(1) != taxon_filter_list.end() || taxon_filter_list.find(0) != taxon_filter_list.end()) - throw std::runtime_error("Option --taxonlist/--taxon-exclude used with invalid argument (0 or 1)."); - for (size_t i = 0; i < taxon_list_->size(); ++i) - if (nodes.contained((*taxon_list_)[i], taxon_filter_list) ^ e) - v->set(i); - return v; -} - const BitVector* DatabaseFile::builtin_filter() { return nullptr; @@ -670,24 +594,24 @@ std::string DatabaseFile::file_name() return InputFile::file_name; } -size_t DatabaseFile::sparse_sequence_count() const +int64_t DatabaseFile::sparse_sequence_count() const { return sequence_count(); } -std::vector DatabaseFile::taxids(size_t oid) const +std::vector DatabaseFile::taxids(size_t oid) const { return (*taxon_list_)[oid]; } void DatabaseFile::seq_data(size_t oid, std::vector& dst) const { - throw std::runtime_error("Operation not supported."); + throw OperationNotSupported(); } size_t DatabaseFile::seq_length(size_t oid) const { - throw std::runtime_error("Operation not supported."); + throw OperationNotSupported(); } void DatabaseFile::init_random_access(const size_t query_block, const size_t ref_blocks, bool dictionary) @@ -701,27 +625,48 @@ void DatabaseFile::end_random_access(bool dictionary) if (!dictionary) return; free_dictionary(); - dict_len_.clear(); - dict_len_.shrink_to_fit(); - dict_title_.clear(); - dict_title_.shrink_to_fit(); - dict_seq_.clear(); - dict_seq_.shrink_to_fit(); } -std::vector DatabaseFile::accession_to_oid(const std::string& acc) const -{ - throw std::runtime_error("Operation not supported."); +void DatabaseFile::init_write() { + throw OperationNotSupported(); } -SequenceFile::LoadTitles DatabaseFile::load_titles() -{ - return LoadTitles::SINGLE_PASS; +void DatabaseFile::write_seq(const Sequence& seq, const std::string& id) { + throw OperationNotSupported(); } -std::vector* DatabaseFile::taxon_scientific_names() { - vector* r = new vector; - seek(header2.taxon_names_offset); - (*this) >> (*r); - return r; +std::string DatabaseFile::taxon_scientific_name(TaxId taxid) const { + return taxid < (TaxId)taxon_scientific_names_.size() && !taxon_scientific_names_[taxid].empty() ? taxon_scientific_names_[taxid] : std::to_string(taxid); +} + +#ifdef EXTRA + +const char* const SEQID_EXTENSION = ".seqid"; + +void DatabaseFile::prep_db() { + config.database.require(); + task_timer timer("Opening the database"); + DatabaseFile db(config.database); + timer.go("Writing seqid list"); + db.make_seqid_list(); // db.file_name() + SEQID_EXTENSION); + timer.go("Closing the database"); + db.close(); +} + +#endif + +void DatabaseFile::read_seqid_list() { + if (flag_any(flags_, Flags::ACC_TO_OID_MAPPING)) + acc2oid_.reserve(sequence_count()); + OId oid = 0; + vector seq; + string id; + init_seq_access(); + for (int64_t n = 0; n < sequence_count(); ++n) { + read_seq(seq, id); + add_seqid_mapping(id, oid++); + } + /*if ((flag_any(flags_, Flags::ACC_TO_OID_MAPPING) && (int64_t)acc2oid_.size() != sequence_count()) + || (flag_any(flags_, Flags::OID_TO_ACC_MAPPING) && (int64_t)acc_.size() != sequence_count())) + throw runtime_error("Inconsistent size of database and seqid file.");*/ } \ No newline at end of file diff --git a/src/data/dmnd/dmnd.h b/src/data/dmnd/dmnd.h index e86ef5ba8..4b5e8a982 100644 --- a/src/data/dmnd/dmnd.h +++ b/src/data/dmnd/dmnd.h @@ -35,14 +35,15 @@ struct ReferenceHeader ReferenceHeader() : magic_number(MAGIC_NUMBER), build(Const::build_version), - db_version(current_db_version), + db_version(current_db_version_prot), sequences(0), letters(0) { } uint64_t magic_number; uint32_t build, db_version; uint64_t sequences, letters, pos_array_offset; - enum { current_db_version = 3 }; + static const uint32_t current_db_version_prot; + static const uint32_t current_db_version_nucl; static constexpr uint64_t MAGIC_NUMBER = 0x24af8a415ee186dllu; friend InputFile& operator>>(InputFile& file, ReferenceHeader& h); }; @@ -54,11 +55,17 @@ struct ReferenceHeader2 taxon_array_size(0), taxon_nodes_offset(0), taxon_names_offset(0) +#ifdef EXTRA + ,db_type(SequenceType::amino_acid) +#endif { memset(hash, 0, sizeof(hash)); } char hash[16]; uint64_t taxon_array_offset, taxon_array_size, taxon_nodes_offset, taxon_names_offset; +#ifdef EXTRA + SequenceType db_type; +#endif friend Serializer& operator<<(Serializer &s, const ReferenceHeader2 &h); friend Deserializer& operator>>(Deserializer &d, ReferenceHeader2 &h); @@ -73,28 +80,32 @@ struct Database_format_exception : public std::exception struct DatabaseFile : public SequenceFile, public InputFile { - DatabaseFile(const string &file_name, Metadata metadata = Metadata(), Flags flags = Flags::NONE); - DatabaseFile(TempFile &tmp_file); + DatabaseFile(const std::string &file_name, Metadata metadata = Metadata(), Flags flags = Flags::NONE, const ValueTraits& value_traits = amino_acid_traits); + DatabaseFile(TempFile &tmp_file, const ValueTraits& value_traits = amino_acid_traits); static void read_header(InputFile &stream, ReferenceHeader &header); - static bool is_diamond_db(const string &file_name); + static bool is_diamond_db(const std::string &file_name); void create_partition(size_t max_letters); virtual void create_partition_balanced(size_t max_letters) override; void create_partition_fixednumber(size_t n); - virtual void save_partition(const string& partition_file_name, const string& annotation = "") override; - void load_partition(const string & partition_file_name); + virtual void save_partition(const std::string& partition_file_name, const std::string& annotation = "") override; + void load_partition(const std::string & partition_file_name); void clear_partition(); - virtual size_t get_n_partition_chunks() override; + virtual int get_n_partition_chunks() override; void skip_seq(); bool has_taxon_id_lists() const; bool has_taxon_nodes() const; bool has_taxon_scientific_names() const; virtual void close() override; - virtual void set_seqinfo_ptr(size_t i) override; - virtual size_t tell_seq() const override; + virtual void set_seqinfo_ptr(OId i) override; + virtual OId tell_seq() const override; + virtual bool eof() const override; virtual void init_seq_access() override; - static void make_db(TempFile** tmp_out = nullptr, std::list* input_file = nullptr); + static void make_db(); +#ifdef EXTRA + static void prep_db(); +#endif enum { min_build_required = 74, MIN_DB_VERSION = 2 }; @@ -110,10 +121,11 @@ struct DatabaseFile : public SequenceFile, public InputFile size_t max_letters; size_t n_seqs_total; - vector chunks; + std::vector chunks; }; Partition partition; + virtual int64_t file_count() const override; virtual void init_seqinfo_access() override; virtual void seek_chunk(const Chunk& chunk) override; virtual SeqInfo read_seqinfo() override; @@ -123,48 +135,37 @@ struct DatabaseFile : public SequenceFile, public InputFile virtual void read_seq_data(Letter* dst, size_t len, size_t& pos, bool seek) override; virtual void read_id_data(const int64_t oid, char* dst, size_t len) override; virtual void skip_id_data() override; - virtual std::string seqid(size_t oid) const override; - virtual std::string dict_title(size_t dict_id, const size_t ref_block) const override; - virtual size_t dict_len(size_t dict_id, const size_t ref_block) const override; - virtual std::vector dict_seq(size_t dict_id, const size_t ref_block) const override; - virtual size_t sequence_count() const override; - virtual void read_seq(std::vector& seq, std::string& id) override; + virtual int64_t sequence_count() const override; + virtual bool read_seq(std::vector& seq, std::string& id, std::vector* quals = nullptr) override; virtual size_t letters() const override; virtual int db_version() const override; virtual int program_build_version() const override; virtual Metadata metadata() const override; - virtual TaxonomyNodes* taxon_nodes() override; - virtual std::vector* taxon_scientific_names() override; + virtual std::string taxon_scientific_name(TaxId taxid) const override; virtual int build_version() override; virtual ~DatabaseFile(); virtual void close_weakly() override; virtual void reopen() override; virtual BitVector* filter_by_accession(const std::string& file_name) override; - virtual BitVector* filter_by_taxonomy(const std::string& include, const std::string& exclude, TaxonomyNodes& nodes) override; virtual const BitVector* builtin_filter() override; virtual std::string file_name() override; - virtual size_t sparse_sequence_count() const override; - virtual std::vector taxids(size_t oid) const override; + virtual int64_t sparse_sequence_count() const override; + virtual std::vector taxids(size_t oid) const override; virtual void seq_data(size_t oid, std::vector& dst) const override; virtual size_t seq_length(size_t oid) const override; virtual void init_random_access(const size_t query_block, const size_t ref_blocks, bool dictionary = true) override; virtual void end_random_access(bool dictionary = true) override; - virtual std::vector accession_to_oid(const std::string& acc) const override; - virtual LoadTitles load_titles() override; + virtual void init_write() override; + virtual void write_seq(const Sequence& seq, const std::string& id) override; static const char* FILE_EXTENSION; private: - virtual void write_dict_entry(size_t block, size_t oid, size_t len, const char* id, const Letter* seq, const double self_aln_score) override; - virtual bool load_dict_entry(InputFile& f, const size_t ref_block) override; - virtual void reserve_dict(const size_t ref_blocks) override; - void init(Flags flags = Flags::NONE); + void read_seqid_list(); std::unique_ptr taxon_list_; - std::vector> dict_len_; - std::vector dict_title_; - std::vector dict_seq_; + std::vector taxon_scientific_names_; }; diff --git a/src/data/enum_seeds.h b/src/data/enum_seeds.h index b55d53e66..5df0d03e7 100644 --- a/src/data/enum_seeds.h +++ b/src/data/enum_seeds.h @@ -1,6 +1,6 @@ #pragma once #include -#include "block.h" +#include "block/block.h" #include "../search/seed_complexity.h" #include "../util/ptr_vector.h" #include "../basic/seed_iterator.h" @@ -11,10 +11,9 @@ template Search::SeedStats enum_seeds(SequenceSet* seqs, F* f, unsigned begin, unsigned end, const Filter* filter, const EnumCfg& cfg) { - vector buf(seqs->max_len(begin, end)); + std::vector buf(seqs->max_len(begin, end)); uint64_t key; Search::SeedStats stats; - const bool freq = config.freq_masking; for (unsigned i = begin; i < end; ++i) { if (cfg.skip && (*cfg.skip)[i / align_mode.query_contexts]) continue; @@ -26,16 +25,11 @@ Search::SeedStats enum_seeds(SequenceSet* seqs, F* f, unsigned begin, unsigned e if (seq.length() < sh.length_) continue; SeedIterator it(buf, sh); Letter* ptr = seqs->ptr(i); - size_t j = 0; + Loc j = 0; while (it.good()) { - //if (freq || Search::seed_is_complex_unreduced(ptr++, sh, cfg.seed_cut, cfg.mask_seeds, stats)) { - if (it.get(key, sh)) - if (filter->contains(key, shape_id)) - (*f)(key, seqs->position(i, j), i, shape_id); - //} - //else { - //++it; - //} + if (it.get(key, sh)) + if (filter->contains(key, shape_id)) + (*f)(key, seqs->position(i, j), i, shape_id); ++j; } } @@ -44,6 +38,33 @@ Search::SeedStats enum_seeds(SequenceSet* seqs, F* f, unsigned begin, unsigned e return stats; } +template +Search::SeedStats enum_seeds_minimizer(SequenceSet* seqs, F* f, unsigned begin, unsigned end, const Filter* filter, const EnumCfg& cfg, Loc it_param) +{ + std::vector buf(seqs->max_len(begin, end)); + Search::SeedStats stats; + for (unsigned i = begin; i < end; ++i) { + if (cfg.skip && (*cfg.skip)[i / align_mode.query_contexts]) + continue; + seqs->convert_to_std_alph(i); + const Sequence seq = (*seqs)[i]; + Reduction::reduce_seq(seq, buf); + for (size_t shape_id = cfg.shape_begin; shape_id < cfg.shape_end; ++shape_id) { + const Shape& sh = shapes[shape_id]; + if (seq.length() < sh.length_) continue; + It it(buf, sh, it_param); + while (it.good()) { + const uint64_t key = *it; + if (filter->contains(key, shape_id)) + (*f)(key, seqs->position(i, it.pos()), i, shape_id); + ++it; + } + } + } + f->finish(); + return stats; +} + template void enum_seeds_hashed(SequenceSet* seqs, F* f, unsigned begin, unsigned end, const Filter* filter, const EnumCfg& cfg) { @@ -57,9 +78,8 @@ void enum_seeds_hashed(SequenceSet* seqs, F* f, unsigned begin, unsigned end, co const Shape& sh = shapes[shape_id]; if (seq.length() < sh.length_) continue; const uint64_t shape_mask = sh.long_mask(); - //const __m128i shape_mask = sh.long_mask_sse_; HashedSeedIterator it(seq, sh); - size_t j = 0; + Loc j = 0; while (it.good()) { if (it.get(key, shape_mask)) { if (filter->contains(key, shape_id)) @@ -83,7 +103,7 @@ void enum_seeds_contiguous(SequenceSet* seqs, F* f, unsigned begin, unsigned end const Sequence seq = (*seqs)[i]; if (seq.length() < It::length()) continue; It it(seq); - size_t j = 0; + Loc j = 0; while (it.good()) { if (it.get(key)) if (filter->contains(key, 0)) @@ -143,6 +163,10 @@ static void enum_seeds_worker(F* f, SequenceSet* seqs, const unsigned begin, con throw std::runtime_error("Unsupported reduction."); } } + else if(cfg->minimizer_window > 0) + *stats = enum_seeds_minimizer(seqs, f, begin, end, filter, *cfg, cfg->minimizer_window); + else if(config.sketch_size > 0) + *stats = enum_seeds_minimizer(seqs, f, begin, end, filter, *cfg, config.sketch_size); else *stats = enum_seeds(seqs, f, begin, end, filter, *cfg); } diff --git a/src/data/fasta/fasta_file.cpp b/src/data/fasta/fasta_file.cpp new file mode 100644 index 000000000..62460ff30 --- /dev/null +++ b/src/data/fasta/fasta_file.cpp @@ -0,0 +1,329 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2021 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#include +#include "fasta_file.h" +#include "../../util/system/system.h" +#include "../../util/seq_file_format.h" +#include "../../basic/config.h" +#include "../../util/sequence/sequence.h" +#include "../../util/string/tokenizer.h" + +using std::vector; +using std::string; +using std::pair; +using std::runtime_error; +using std::tie; +using namespace Util::Tsv; + +FastaFile::FastaFile(const std::vector& file_name, Metadata metadata, Flags flags, const ValueTraits& value_traits): + SequenceFile(SequenceFile::Type::FASTA, Alphabet::STD, flags, FormatFlags::DICT_LENGTHS | FormatFlags::DICT_SEQIDS, value_traits), + oid_(0) +{ + if (file_name.size() > 2) + throw OperationNotSupported(); + for (const auto& f : file_name) + file_.emplace_back(f); + file_ptr_ = file_.begin(); + format_ = guess_format(file_.front()); + if (!flag_any(flags, Flags::NEED_LETTER_COUNT)) + return; +#if EXTRA + /*if (exists(file_name.front() + ".fai")) { + task_timer timer("Reading faidx file", 3); + int64_t seqs = 0, letters = 0; + string acc; + for (const auto& f : file_name) { + pair r = read_fai_file(f + ".fai", seqs, letters); + seqs += r.first; + letters += r.second; + } + seqs_ = seqs; + letters_ = letters; + } + else {*/ +#endif + task_timer timer("Reading fasta file", 3); + tie(seqs_, letters_) = init_read(); + set_seqinfo_ptr(0); +#ifdef EXTRA + //} +#endif +} + +FastaFile::FastaFile(const string& file_name, bool overwrite, const WriteAccess&): + SequenceFile(SequenceFile::Type::FASTA, Alphabet::STD, Flags::NONE, FormatFlags::DICT_LENGTHS | FormatFlags::DICT_SEQIDS, amino_acid_traits), + out_file_(file_name.empty() ? new TempFile : new OutputFile(file_name, Compressor::NONE, overwrite ? "w+b" : "r+b")), + format_(new FASTA_format()), + oid_(0), + seqs_(0), + letters_(0) +{ + file_.emplace_back(*out_file_); + file_ptr_ = file_.begin(); + if (!overwrite) { + vector seq; + string id; + while (read_seq(seq, id, nullptr)) { + ++seqs_; + letters_ += seq.size(); + } + } +} + +int64_t FastaFile::file_count() const { + return file_.size(); +} + +bool FastaFile::files_synced() { + if (file_ptr_ != file_.begin()) + return false; + if (file_ptr_->eof()) { + string id; + vector seq; + if (++file_ptr_ != file_.end() && format_->get_seq(id, seq, *file_ptr_, value_traits_, nullptr)) + return false; + } + return true; +} + +SequenceFile::SeqInfo FastaFile::read_seqinfo() { + throw OperationNotSupported(); +} + +void FastaFile::putback_seqinfo() { + throw OperationNotSupported(); +} + +void FastaFile::close() { + if (out_file_) + file_.front().close(); + else + for (auto& f : file_) + if (f.temp_file) + f.close_and_delete(); + else + f.close(); + +} + +void FastaFile::set_seqinfo_ptr(OId i) { + if (out_file_) + out_file_->rewind(); + for (auto& f : file_) + f.rewind(); + oid_ = 0; + vector seq; + string id; + while (oid_ != i) { + read_seq(seq, id, nullptr); + } +} + +OId FastaFile::tell_seq() const { + return oid_; +} + +bool FastaFile::eof() const { + return file_.back().eof(); +} + +void FastaFile::init_seq_access() { + set_seqinfo_ptr(0); +} + +bool FastaFile::read_seq(vector& seq, string &id, std::vector* quals) +{ + oid_++; + const bool r = format_->get_seq(id, seq, *(file_ptr_++), this->value_traits_, quals); + if (file_ptr_ == file_.end()) + file_ptr_ = file_.begin(); + return r; +} + +void FastaFile::create_partition_balanced(size_t max_letters) { + throw OperationNotSupported(); +} + +void FastaFile::save_partition(const string & partition_file_name, const string & annotation) { + throw OperationNotSupported(); +} + +int FastaFile::get_n_partition_chunks() { + throw OperationNotSupported(); +} + +void FastaFile::init_seqinfo_access() { + throw OperationNotSupported(); +} + +void FastaFile::seek_chunk(const Chunk& chunk) { + throw OperationNotSupported(); +} + +size_t FastaFile::id_len(const SeqInfo& seq_info, const SeqInfo& seq_info_next) { + throw OperationNotSupported(); +} + +void FastaFile::seek_offset(size_t p) { +} + +void FastaFile::read_seq_data(Letter* dst, size_t len, size_t& pos, bool seek) { + throw OperationNotSupported(); +} + +void FastaFile::read_id_data(const int64_t oid, char* dst, size_t len) { + throw OperationNotSupported(); +} + +void FastaFile::skip_id_data() { + throw OperationNotSupported(); +} + +int64_t FastaFile::sequence_count() const { + return seqs_; +} + +size_t FastaFile::letters() const { + return letters_; +} + +int FastaFile::db_version() const { + throw OperationNotSupported(); +} + +int FastaFile::program_build_version() const { + throw OperationNotSupported(); +} + +SequenceFile::Metadata FastaFile::metadata() const { + return Metadata(); +} + +int FastaFile::build_version() { + throw OperationNotSupported(); +} + +FastaFile::~FastaFile() +{ + close(); +} + +void FastaFile::close_weakly() +{ +} + +void FastaFile::reopen() +{ +} + +BitVector* FastaFile::filter_by_accession(const std::string& file_name) +{ + throw std::runtime_error("The FASTA database format does not support filtering by accession."); +} + +const BitVector* FastaFile::builtin_filter() +{ + return nullptr; +} + +std::string FastaFile::file_name() +{ + return file_.front().file_name; +} + +int64_t FastaFile::sparse_sequence_count() const +{ + throw OperationNotSupported(); +} + +std::vector FastaFile::taxids(size_t oid) const +{ + throw OperationNotSupported(); +} + +void FastaFile::seq_data(size_t oid, std::vector& dst) const +{ + throw OperationNotSupported(); +} + +size_t FastaFile::seq_length(size_t oid) const +{ + throw OperationNotSupported(); +} + +void FastaFile::init_random_access(const size_t query_block, const size_t ref_blocks, bool dictionary) +{ + if (dictionary) + load_dictionary(query_block, ref_blocks); +} + +void FastaFile::end_random_access(bool dictionary) +{ + if (!dictionary) + return; + free_dictionary(); +} + +void FastaFile::init_write() { + out_file_->seek(0, SEEK_END); +} + +void FastaFile::write_seq(const Sequence& seq, const std::string& id) { + Util::Seq::format(seq, id.c_str(), nullptr, *out_file_, "fasta", value_traits_); + ++seqs_; + letters_ += seq.length(); +} + +void FastaFile::prep_db(const string& path) { + task_timer timer("Indexing FASTA file"); + TextInputFile f(path); + FASTA_format fmt; + vector seq; + string id; + std::ofstream out(path + ".fai"); + int64_t seqs = 0, letters = 0; + while (fmt.get_seq(id, seq, f, amino_acid_traits)) { + out << Util::Seq::seqid(id.c_str(), false) << '\t' << seq.size() << std::endl; + ++seqs; + letters += seq.size(); + } + f.close(); + out.close(); + timer.finish(); + message_stream << "Processed " << seqs << " sequences, " << letters << " total letters." << std::endl; +} + +int64_t FastaFile::line_count() const { + return file_.front().line_count; +} + +std::pair FastaFile::init_read() { + vector seq; + string id; + int64_t seqs = 0, letters = 0; + while (read_seq(seq, id)) { + if (flag_any(flags_, Flags::ACC_TO_OID_MAPPING | Flags::OID_TO_ACC_MAPPING)) + add_seqid_mapping(id, seqs); + ++seqs; + letters += seq.size(); + } + return { seqs, letters }; +} \ No newline at end of file diff --git a/src/data/fasta/fasta_file.h b/src/data/fasta/fasta_file.h new file mode 100644 index 000000000..c57d6ebb4 --- /dev/null +++ b/src/data/fasta/fasta_file.h @@ -0,0 +1,69 @@ +#include +#include +#include "../sequence_file.h" + +struct FastaFile : public SequenceFile +{ + + struct WriteAccess {}; + + FastaFile(const std::vector &file_name, Metadata metadata = Metadata(), Flags flags = Flags::NONE, const ValueTraits& value_traits = amino_acid_traits); + FastaFile(const std::string& file_name, bool overwrite, const WriteAccess&); + + virtual int64_t file_count() const override; + virtual void create_partition_balanced(size_t max_letters) override; + virtual void save_partition(const std::string& partition_file_name, const std::string& annotation = "") override; + virtual int get_n_partition_chunks() override; + virtual void close() override; + virtual void set_seqinfo_ptr(OId i) override; + virtual OId tell_seq() const override; + virtual bool eof() const override; + virtual bool files_synced() override; + virtual void init_seq_access() override; + virtual void init_seqinfo_access() override; + virtual void seek_chunk(const Chunk& chunk) override; + virtual SeqInfo read_seqinfo() override; + virtual void putback_seqinfo() override; + virtual size_t id_len(const SeqInfo& seq_info, const SeqInfo& seq_info_next) override; + virtual void seek_offset(size_t p) override; + virtual void read_seq_data(Letter* dst, size_t len, size_t& pos, bool seek) override; + virtual void read_id_data(const int64_t oid, char* dst, size_t len) override; + virtual void skip_id_data() override; + virtual int64_t sequence_count() const override; + virtual bool read_seq(std::vector& seq, std::string& id, std::vector* quals = nullptr) override; + virtual size_t letters() const override; + virtual int db_version() const override; + virtual int program_build_version() const override; + virtual Metadata metadata() const override; + virtual int build_version() override; + virtual ~FastaFile(); + virtual void close_weakly() override; + virtual void reopen() override; + virtual BitVector* filter_by_accession(const std::string& file_name) override; + virtual const BitVector* builtin_filter() override; + virtual std::string file_name() override; + virtual int64_t sparse_sequence_count() const override; + virtual std::vector taxids(size_t oid) const override; + virtual void seq_data(size_t oid, std::vector& dst) const override; + virtual size_t seq_length(size_t oid) const override; + virtual void init_random_access(const size_t query_block, const size_t ref_blocks, bool dictionary = true) override; + virtual void end_random_access(bool dictionary = true) override; + virtual void init_write() override; + virtual void write_seq(const Sequence& seq, const std::string& id) override; + + int64_t line_count() const; + + static void prep_db(const std::string& path); + +private: + + std::pair init_read(); + + std::list file_; + std::list::iterator file_ptr_; + std::unique_ptr out_file_; + std::unique_ptr format_; + OId oid_; + int64_t seqs_, letters_; + +}; diff --git a/src/data/flags.h b/src/data/flags.h index dece3d6c9..f9a62f3e2 100644 --- a/src/data/flags.h +++ b/src/data/flags.h @@ -18,10 +18,10 @@ extern NoFilter no_filter; #ifdef KEEP_TARGET_ID struct SeedLoc { - Value() {} - Value(PackedLoc pos) : + SeedLoc() {} + SeedLoc(PackedLoc pos) : pos(pos) {} - Value(PackedLoc pos, uint32_t block_id) : + SeedLoc(PackedLoc pos, uint32_t block_id) : pos(pos), block_id(block_id) {} @@ -43,4 +43,5 @@ struct EnumCfg { const bool filter_masked_seeds, mask_seeds; const double seed_cut; const MaskingAlgo soft_masking; + const Loc minimizer_window; }; diff --git a/src/data/frequent_seeds.cpp b/src/data/frequent_seeds.cpp index fcccaa229..38e1aaf80 100644 --- a/src/data/frequent_seeds.cpp +++ b/src/data/frequent_seeds.cpp @@ -30,6 +30,7 @@ along with this program. If not, see . using std::endl; using std::atomic; +using std::vector; const double Frequent_seeds::hash_table_factor = 1.3; Frequent_seeds frequent_seeds; @@ -93,7 +94,7 @@ void Frequent_seeds::build(unsigned sid, const SeedPartitionRange &range, Double vector ref_sds(range.size()), query_sds(range.size()); atomic seedp(range.begin()); vector threads; - for (unsigned i = 0; i < config.threads_; ++i) + for (int i = 0; i < config.threads_; ++i) threads.emplace_back(compute_sd, &seedp, query_seed_hits, ref_seed_hits, &ref_sds, &query_sds); for (auto &t : threads) t.join(); @@ -109,7 +110,7 @@ void Frequent_seeds::build(unsigned sid, const SeedPartitionRange &range, Double } void Frequent_seeds::clear_masking(SequenceSet& seqs) { - for (size_t i = 0; i < seqs.size(); ++i) { + for (BlockId i = 0; i < seqs.size(); ++i) { const size_t len = seqs.length(i); Letter* p = seqs.ptr(i), *end = p + len; for (; p < end; ++p) diff --git a/src/data/index.cpp b/src/data/index.cpp index 6d1d3f077..ef328756a 100644 --- a/src/data/index.cpp +++ b/src/data/index.cpp @@ -13,13 +13,13 @@ void makeindex() { if (db.ref_header.letters > MAX_LETTERS) throw std::runtime_error("Indexing is only supported for databases of < 100000000 letters."); - ::shapes = ShapeConfig(config.shape_mask.empty() ? Search::shape_codes.at(config.sensitivity) : config.shape_mask, config.shapes); + ::shapes = ShapeConfig(config.shape_mask.empty() ? Search::shape_codes[(int)align_mode.sequence_type].at(config.sensitivity) : config.shape_mask, config.shapes); config.algo = Config::Algo::DOUBLE_INDEXED; - Block* block = db.load_seqs(MAX_LETTERS, false); + Block* block = db.load_seqs(MAX_LETTERS, nullptr, SequenceFile::LoadFlags::SEQS); task_timer timer("Building index"); - HashedSeedSet index(*block, nullptr, 0.0, Search::soft_masking_algo(Search::sensitivity_traits.at(config.sensitivity))); + HashedSeedSet index(*block, nullptr, 0.0, Search::soft_masking_algo(Search::sensitivity_traits[(int)align_mode.sequence_type].at(config.sensitivity))); timer.go("Writing to disk"); OutputFile out(db.file_name() + ".seed_idx"); diff --git a/src/data/queries.cpp b/src/data/queries.cpp index bb69a84fd..ef7bdfec0 100644 --- a/src/data/queries.cpp +++ b/src/data/queries.cpp @@ -23,8 +23,7 @@ along with this program. If not, see . using std::unique_ptr; -unsigned current_query_chunk; -vector query_aligned; +std::vector query_aligned; std::mutex query_aligned_mtx; unique_ptr query_seeds_hashed; unique_ptr query_seeds_bitset; @@ -32,7 +31,6 @@ unique_ptr query_seeds_bitset; void write_unaligned(const Block& query, OutputFile *file) { const size_t n = query.ids().size(); - TextBuffer buf; for (size_t i = 0; i < n; ++i) { if (!query_aligned[i]) { Util::Seq::format(align_mode.query_translated ? query.source_seqs()[i] : query.seqs()[i], @@ -48,7 +46,6 @@ void write_unaligned(const Block& query, OutputFile *file) void write_aligned(const Block& query, OutputFile *file) { const size_t n = query.ids().size(); - TextBuffer buf; for (size_t i = 0; i < n; ++i) { if (query_aligned[i]) { Util::Seq::format(align_mode.query_translated ? query.source_seqs()[i] : query.seqs()[i], diff --git a/src/data/queries.h b/src/data/queries.h index 30d58543b..007cb18d7 100644 --- a/src/data/queries.h +++ b/src/data/queries.h @@ -23,15 +23,12 @@ along with this program. If not, see . #pragma once #include #include +#include #include "../util/io/output_file.h" -#include "block.h" - -using std::vector; - -extern unsigned current_query_chunk; +#include "block/block.h" extern std::mutex query_aligned_mtx; -extern vector query_aligned; +extern std::vector query_aligned; void write_unaligned(const Block& query, OutputFile *file); void write_aligned(const Block& query, OutputFile *file); diff --git a/src/data/reference.cpp b/src/data/reference.cpp deleted file mode 100644 index 9f844f3eb..000000000 --- a/src/data/reference.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "reference.h" - -int64_t current_ref_block; -bool blocked_processing; diff --git a/src/data/reference.h b/src/data/reference.h index 8ac5a3487..66e2a9fbd 100644 --- a/src/data/reference.h +++ b/src/data/reference.h @@ -26,8 +26,5 @@ along with this program. If not, see . #include "sequence_file.h" #include "seed_histogram.h" -extern int64_t current_ref_block; -extern bool blocked_processing; - -Chunk to_chunk(const string& line); -string to_string(const Chunk& c); \ No newline at end of file +Chunk to_chunk(const std::string& line); +std::string to_string(const Chunk& c); \ No newline at end of file diff --git a/src/data/seed_array.cpp b/src/data/seed_array.cpp index 2fff1d7d5..314762a35 100644 --- a/src/data/seed_array.cpp +++ b/src/data/seed_array.cpp @@ -28,20 +28,21 @@ along with this program. If not, see . #include "../search/seed_complexity.h" using std::array; +using std::vector; typedef vector> PtrSet; -char* SeedArray::alloc_buffer(const SeedHistogram &hst, size_t index_chunks) +char* SeedArray::alloc_buffer(const SeedHistogram &hst, int index_chunks) { return new char[sizeof(Entry) * hst.max_chunk_size(index_chunks)]; } -static size_t seed_bits(const SeedEncoding code) { +static int seed_bits(const SeedEncoding code) { switch (code) { case SeedEncoding::HASHED: - return sizeof(SeedArray::Entry::Key) * 8; + return int(sizeof(SeedOffset) * 8); case SeedEncoding::SPACED_FACTOR: - return ceil(shapes[0].weight_ * Reduction::reduction.bit_size_exact()) - Const::seedp_bits; + return int(ceil(shapes[0].weight_ * Reduction::reduction.bit_size_exact()) - Const::seedp_bits); case SeedEncoding::CONTIGUOUS: return shapes[0].length_ * Reduction::reduction.bit_size() - Const::seedp_bits; default: @@ -58,7 +59,7 @@ struct BufferedWriter memset(n, 0, sizeof(n)); memcpy(this->ptr, ptr, sizeof(this->ptr)); } - void push(Packed_seed key, int64_t value, uint32_t block_id, const SeedPartitionRange &range) + void push(PackedSeed key, int64_t value, uint32_t block_id, const SeedPartitionRange &range) { const unsigned p = seed_partition(key); if (range.contains(p)) { @@ -127,7 +128,7 @@ SeedArray::SeedArray(Block &seqs, const ShapeHistogram &hst, const SeedPartition if (enum_cfg.shape_end - enum_cfg.shape_begin > 1) throw std::runtime_error("SeedArray construction for >1 shape."); begin_[range.begin()] = 0; - for (size_t i = range.begin(); i < range.end(); ++i) + for (int i = range.begin(); i < range.end(); ++i) begin_[i + 1] = begin_[i] + partition_size(hst, i); PtrSet iterators(build_iterators(*this, hst)); @@ -149,7 +150,7 @@ struct BufferedWriter2 { memset(n, 0, sizeof(n)); } - void push(Packed_seed key, int64_t value, const SeedPartitionRange& range) + void push(PackedSeed key, int64_t value, const SeedPartitionRange& range) { const unsigned p = seed_partition(key); if (range.contains(p)) { diff --git a/src/data/seed_array.h b/src/data/seed_array.h index 6e0944787..f33f79e51 100644 --- a/src/data/seed_array.h +++ b/src/data/seed_array.h @@ -24,7 +24,6 @@ along with this program. If not, see . #include "flags.h" #pragma pack(1) -// #define KEEP_TARGET_ID struct Block; @@ -39,11 +38,11 @@ struct SeedArray key(), value() { } - Entry(unsigned key, Loc value) : + Entry(SeedOffset key, Loc value) : key(key), value(value) { } - Entry(unsigned key, Loc pos, uint32_t block_id): + Entry(SeedOffset key, Loc pos, uint32_t block_id): key(key), #ifdef KEEP_TARGET_ID value(pos, block_id) @@ -51,7 +50,7 @@ struct SeedArray value(pos) #endif {} - uint32_t key; + SeedOffset key; struct GetKey { uint32_t operator()(const Entry& e) const { @@ -59,9 +58,9 @@ struct SeedArray } }; - using Key = uint32_t; - using Value = SeedLoc; SeedLoc value; + using Key = decltype(key); + using Value = decltype(value); } PACKED_ATTRIBUTE; template @@ -110,9 +109,9 @@ struct SeedArray return stats_; } - static char *alloc_buffer(const SeedHistogram &hst, size_t index_chunks); + static char *alloc_buffer(const SeedHistogram &hst, int index_chunks); - const size_t key_bits; + const int key_bits; private: diff --git a/src/data/seed_histogram.cpp b/src/data/seed_histogram.cpp index bc2c984cc..71ba4e167 100644 --- a/src/data/seed_histogram.cpp +++ b/src/data/seed_histogram.cpp @@ -24,28 +24,30 @@ along with this program. If not, see . #include "../util/util.h" #include "../util/algo/partition.h" #include "../basic/shape_config.h" -#include "block.h" +#include "block/block.h" #include "../util/ptr_vector.h" #include "enum_seeds.h" #include "seed_set.h" +using std::vector; + SeedPartitionRange current_range; SeedHistogram::SeedHistogram() { } -size_t SeedHistogram::max_chunk_size(size_t index_chunks) const +size_t SeedHistogram::max_chunk_size(const int index_chunks) const { size_t max = 0; - ::Partition p(Const::seedp, index_chunks); + ::Partition p(Const::seedp, index_chunks); for (unsigned shape = 0; shape < shapes.count(); ++shape) - for (unsigned chunk = 0; chunk < p.parts; ++chunk) + for (int chunk = 0; chunk < p.parts; ++chunk) max = std::max(max, hst_size(data_[shape], SeedPartitionRange(p.begin(chunk), p.end(chunk)))); return max; } template -SeedHistogram::SeedHistogram(Block& seqs, bool serial, const Filter* filter, SeedEncoding code, const std::vector* skip, const bool mask_seeds, const double seed_cut, const MaskingAlgo soft_masking) : +SeedHistogram::SeedHistogram(Block& seqs, bool serial, const Filter* filter, SeedEncoding code, const std::vector* skip, const bool mask_seeds, const double seed_cut, const MaskingAlgo soft_masking, Loc minimizer_window) : data_(shapes.count()), p_(seqs.seqs().partition(config.threads_)) { @@ -76,15 +78,15 @@ SeedHistogram::SeedHistogram(Block& seqs, bool serial, const Filter* filter, See cb.push_back(new Callback(i, data_)); if (serial) for (unsigned s = 0; s < shapes.count(); ++s) { - const EnumCfg cfg{ &p_,s,s + 1, code, skip, false, mask_seeds, seed_cut, soft_masking }; + const EnumCfg cfg{ &p_,s,s + 1, code, skip, false, mask_seeds, seed_cut, soft_masking, minimizer_window }; enum_seeds(seqs, cb, filter, cfg); } else { - const EnumCfg cfg{ &p_, 0, shapes.count(), code, skip, false, mask_seeds, seed_cut, soft_masking }; + const EnumCfg cfg{ &p_, 0, shapes.count(), code, skip, false, mask_seeds, seed_cut, soft_masking, minimizer_window }; enum_seeds(seqs, cb, filter, cfg); } } -template SeedHistogram::SeedHistogram(Block&, bool, const NoFilter*, SeedEncoding, const std::vector*, const bool, const double, const MaskingAlgo); -template SeedHistogram::SeedHistogram(Block&, bool, const SeedSet*, SeedEncoding, const std::vector*, const bool, const double, const MaskingAlgo); -template SeedHistogram::SeedHistogram(Block&, bool, const HashedSeedSet*, SeedEncoding, const std::vector*, const bool, const double, const MaskingAlgo); \ No newline at end of file +template SeedHistogram::SeedHistogram(Block&, bool, const NoFilter*, SeedEncoding, const std::vector*, const bool, const double, const MaskingAlgo, Loc); +template SeedHistogram::SeedHistogram(Block&, bool, const SeedSet*, SeedEncoding, const std::vector*, const bool, const double, const MaskingAlgo, Loc); +template SeedHistogram::SeedHistogram(Block&, bool, const HashedSeedSet*, SeedEncoding, const std::vector*, const bool, const double, const MaskingAlgo, Loc); \ No newline at end of file diff --git a/src/data/seed_histogram.h b/src/data/seed_histogram.h index 52a2e2725..44585dfa7 100644 --- a/src/data/seed_histogram.h +++ b/src/data/seed_histogram.h @@ -87,12 +87,12 @@ struct SeedHistogram SeedHistogram(); template - SeedHistogram(Block& seqs, bool serial, const Filter* filter, SeedEncoding code, const std::vector* skip, const bool mask_seeds, const double seed_cut, const MaskingAlgo soft_masking); + SeedHistogram(Block& seqs, bool serial, const Filter* filter, SeedEncoding code, const std::vector* skip, const bool mask_seeds, const double seed_cut, const MaskingAlgo soft_masking, Loc minimizer_window); const ShapeHistogram& get(unsigned sid) const { return data_[sid]; } - size_t max_chunk_size(size_t index_chunks) const; + size_t max_chunk_size(const int index_chunks) const; const std::vector& partition() const { diff --git a/src/data/seed_set.cpp b/src/data/seed_set.cpp index 7fe5ea38f..f75b65e9e 100644 --- a/src/data/seed_set.cpp +++ b/src/data/seed_set.cpp @@ -19,7 +19,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ - +#define NOMINMAX +#include "../lib/mio/mmap.hpp" #include #include "seed_set.h" #include "../util/ptr_vector.h" @@ -32,9 +33,11 @@ along with this program. If not, see . static const size_t PADDING = 32; static const double HASH_TABLE_FACTOR = 1.25; +using std::vector; using std::endl; using std::get; using std::runtime_error; +using std::string; NoFilter no_filter; @@ -69,7 +72,7 @@ SeedSet::SeedSet(Block &seqs, double max_coverage, const std::vector* skip PtrVector v; v.push_back(new Seed_set_callback(data_, size_t(max_coverage*pow(Reduction::reduction.size(), shapes[0].length_)))); const auto p = seqs.seqs().partition(1); - const EnumCfg cfg{ &p, 0, 1, SeedEncoding::CONTIGUOUS, skip, true, false, seed_cut, soft_masking }; + const EnumCfg cfg{ &p, 0, 1, SeedEncoding::CONTIGUOUS, skip, true, false, seed_cut, soft_masking, 0 }; enum_seeds(seqs, v, &no_filter, cfg); coverage_ = (double)v.back().coverage / pow(Reduction::reduction.size(), shapes[0].length_); } @@ -96,7 +99,7 @@ HashedSeedSet::HashedSeedSet(Block &seqs, const std::vector* skip, const d PtrVector v; v.push_back(new Hashed_seed_set_callback(data_)); const auto p = seqs.seqs().partition(1); - const EnumCfg cfg{ &p, 0, shapes.count(), SeedEncoding::HASHED, skip, false, false, seed_cut, soft_masking }; + const EnumCfg cfg{ &p, 0, shapes.count(), SeedEncoding::HASHED, skip, false, false, seed_cut, soft_masking, 0 }; enum_seeds(seqs, v, &no_filter, cfg); vector sizes; diff --git a/src/data/seed_set.h b/src/data/seed_set.h index ac14e1fde..098bdd8de 100644 --- a/src/data/seed_set.h +++ b/src/data/seed_set.h @@ -21,12 +21,10 @@ along with this program. If not, see . #pragma once #include -#include "../util/hash_table.h" #include "../util/ptr_vector.h" #include "../util/data_structures/hash_set.h" #include "../masking/masking.h" -#define NOMINMAX -#include "../lib/mio/mmap.hpp" +#include "../lib/mio/forward.h" const uint64_t SEED_INDEX_MAGIC_NUMBER = 0x2d6ba306ecbf6aba; const uint32_t SEED_INDEX_VERSION = 0; diff --git a/src/data/sequence_file.cpp b/src/data/sequence_file.cpp index e6a51dc76..533c40fc8 100644 --- a/src/data/sequence_file.cpp +++ b/src/data/sequence_file.cpp @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2022 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -20,6 +20,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ +#include #include #include #include "sequence_file.h" @@ -32,6 +33,11 @@ along with this program. If not, see . #include "../util/sequence/sequence.h" #include "../util/parallel/multiprocessing.h" #include "../basic/config.h" +#include "fasta/fasta_file.h" +#include "../util/string/tokenizer.h" +#include "../util/tsv/file.h" +#define _REENTRANT +#include "../lib/ips4o/ips4o.hpp" #ifdef WITH_BLASTDB #include "blastdb/blastdb.h" #endif @@ -40,8 +46,27 @@ using std::cout; using std::endl; using std::setw; using std::thread; - -const EMap EnumTraits::to_string = { {SequenceFile::Type::DMND, "Diamond database" }, {SequenceFile::Type::BLAST, "BLAST database"} }; +using std::pair; +using std::tie; +using std::runtime_error; +using std::ofstream; +using std::greater; +using std::numeric_limits; +using std::tuple; +using std::get; +using std::unique_ptr; +using namespace Util::Tsv; + +static constexpr int64_t CHECK_FOR_DNA_COUNT = 10; +const char* const SequenceFile::SEQID_HDR = "seqid"; +const DictId SequenceFile::DICT_EMPTY = std::numeric_limits::max(); + +const EMap EnumTraits::to_string = { + {SequenceFile::Type::DMND, "Diamond database" }, + {SequenceFile::Type::BLAST, "BLAST database"}, + {SequenceFile::Type::FASTA, "FASTA file"}, + {SequenceFile::Type::BLOCK, ""} +}; static string dict_file_name(const size_t query_block, const size_t target_block) { const string file_name = append_label("ref_dict_", query_block) + append_label("_", target_block); @@ -49,7 +74,7 @@ static string dict_file_name(const size_t query_block, const size_t target_block } static size_t single_oid(const SequenceFile* f, const string& acc) { - const vector oid = f->accession_to_oid(acc); + const vector oid = f->accession_to_oid(acc); if (oid.empty()) throw AccessionNotFound(); if (oid.size() > 1) @@ -57,57 +82,38 @@ static size_t single_oid(const SequenceFile* f, const string& acc) { return oid.front(); } -void SequenceFile::load_block(size_t block_id_begin, size_t block_id_end, size_t pos, bool use_filter, const vector* filtered_pos, bool load_ids, Block* block) { - static const size_t MAX_LOAD_SIZE = 2 * GIGABYTES; - seek_offset(pos); - size_t load_size = 0; - for (size_t i = block_id_begin; i < block_id_end; ++i) { - bool seek = false; - if (use_filter && (*filtered_pos)[i]) { - pos = (*filtered_pos)[i]; - seek = true; - } - const size_t l = block->seqs_.length(i); - load_size += l; - read_seq_data(block->seqs_.ptr(i), l, pos, seek); - if (load_ids) - read_id_data(pos - 1, block->ids_.ptr(i), block->ids_.length(i)); - else - skip_id_data(); - if (type_ == Type::DMND) - Masking::get().remove_bit_mask(block->seqs_.ptr(i), block->seqs_.length(i)); - if (load_size > MAX_LOAD_SIZE) { - close_weakly(); - reopen(); - load_size = 0; - } - } +bool SequenceFile::files_synced() { + throw OperationNotSupported(); } -size_t SequenceFile::dict_block(const size_t ref_block) -{ - return config.multiprocessing ? ref_block : 0; +void SequenceFile::init_write() { + throw OperationNotSupported(); } -Block* SequenceFile::load_seqs(const size_t max_letters, bool load_ids, const BitVector* filter, bool fetch_seqs, bool lazy_masking, const Chunk& chunk) -{ - task_timer timer("Loading reference sequences"); - reopen(); +void SequenceFile::write_seq(const Sequence& seq, const std::string& id) { + throw OperationNotSupported(); +} - if(max_letters == 0) - seek_chunk(chunk); +std::string SequenceFile::taxon_scientific_name(TaxId taxid) const { + throw OperationNotSupported(); +} + +pair SequenceFile::load_twopass(const int64_t max_letters, const BitVector* filter, LoadFlags flags, const Chunk& chunk) { init_seqinfo_access(); - size_t database_id = tell_seq(); - size_t letters = 0, seqs = 0, id_letters = 0, seqs_processed = 0, filtered_seq_count = 0; - vector filtered_pos; + OId database_id = tell_seq(); + int64_t letters = 0, seqs = 0, id_letters = 0, seqs_processed = 0, filtered_seq_count = 0; + vector filtered_pos; Block* block = new Block(alphabet_); - + SeqInfo r = read_seqinfo(); size_t offset = r.pos; bool last = false; - if (type() == Type::BLAST && sequence_count() != sparse_sequence_count()) + if (type() == Type::BLAST && sequence_count() != sparse_sequence_count()) { + if (filter) + throw std::runtime_error("Database filtering is not compatible with a BLAST alias database."); filter = builtin_filter(); + } const bool use_filter = filter && !filter->empty(); auto goon = [&]() { @@ -121,17 +127,17 @@ Block* SequenceFile::load_seqs(const size_t max_letters, bool load_ids, const Bi SeqInfo r_next = read_seqinfo(); if (!use_filter || filter->get(database_id)) { letters += r.seq_len; - if (fetch_seqs) { + if (flag_any(flags, LoadFlags::SEQS)) { block->seqs_.reserve(r.seq_len); } - if (load_ids) { + if (flag_any(flags, LoadFlags::TITLES)) { const size_t id_len = this->id_len(r, r_next); id_letters += id_len; - if (fetch_seqs) + if (flag_any(flags, LoadFlags::SEQS)) block->ids_.reserve(id_len); } ++filtered_seq_count; - block->block2oid_.push_back((unsigned)database_id); + block->block2oid_.push_back(database_id); if (use_filter) { filtered_pos.push_back(last ? 0 : r.pos); } @@ -149,38 +155,126 @@ Block* SequenceFile::load_seqs(const size_t max_letters, bool load_ids, const Bi putback_seqinfo(); if (seqs == 0 || filtered_seq_count == 0) - return block; + return { block, seqs_processed }; - if (fetch_seqs) { + if (flag_any(flags, LoadFlags::SEQS)) { block->seqs_.finish_reserve(); - if (load_ids) block->ids_.finish_reserve(); - if (false && type_ == Type::BLAST && config.algo == Config::Algo::QUERY_INDEXED && config.threads_ > 1 && !use_filter) { - assert(!use_filter); - Partition p(filtered_seq_count, config.threads_); - vector t; - for (size_t i = 0; i < p.parts; ++i) - t.emplace_back(&SequenceFile::load_block, this, p.begin(i), p.end(i), offset + p.begin(i), use_filter, &filtered_pos, false, block); - for (std::thread& i : t) - i.join(); + if (flag_any(flags, LoadFlags::TITLES)) block->ids_.finish_reserve(); + + static const size_t MAX_LOAD_SIZE = 2 * GIGABYTES; + if (use_filter && !flag_all(format_flags_, FormatFlags::SEEKABLE)) + throw OperationNotSupported(); + seek_offset(offset); + size_t load_size = 0; + for (BlockId i = 0; i < filtered_seq_count; ++i) { + bool seek = false; + if (use_filter && filtered_pos[i]) { + offset = filtered_pos[i]; + seek = true; + } + const size_t l = block->seqs_.length(i); + load_size += l; + read_seq_data(block->seqs_.ptr(i), l, offset, seek); + if (flag_any(flags, LoadFlags::TITLES)) + read_id_data(block->block2oid_[i], block->ids_.ptr(i), block->ids_.length(i)); + else + skip_id_data(); + if (type_ == Type::DMND) + Masking::get().remove_bit_mask(block->seqs_.ptr(i), block->seqs_.length(i)); + if (load_size > MAX_LOAD_SIZE) { + close_weakly(); + reopen(); + load_size = 0; + } } - else - load_block(0, filtered_seq_count, offset, use_filter, &filtered_pos, load_ids, block); - timer.finish(); - block->seqs_.print_stats(); } + return { block, seqs_processed }; +} - if (config.multiprocessing || config.global_ranking_targets) - blocked_processing = true; - else - blocked_processing = seqs_processed < sequence_count(); +static int frame_mask() { + if (config.query_strands == "both") + return (1 << 6) - 1; + else if (config.query_strands == "plus") + return (1 << 3) - 1; + else if (config.query_strands == "minus") + return ((1 << 3) - 1) << 3; + throw std::runtime_error("frame_mask"); +} + +std::pair SequenceFile::load_onepass(const int64_t max_letters, const BitVector* filter, LoadFlags flags) { + vector seq; + string id; + vector qual; + int64_t letters = 0, seq_count = 0; + Block* block = new Block(alphabet_); + const bool load_seqs = flag_any(flags, LoadFlags::SEQS), load_titles = flag_any(flags, LoadFlags::TITLES), preserve_dna = flag_any(flags,LoadFlags::DNA_PRESERVATION); + vector* q = flag_any(flags, LoadFlags::QUALITY) ? &qual : nullptr; + OId oid = tell_seq(); + const int frame_mask = ::frame_mask(); + const int64_t modulo = file_count(); + do { + if (!read_seq(seq, id, q)) + break; + if (seq.size() == 0) + continue; + if (filter && !filter->get(oid)) { + ++oid; + continue; + } + + letters += block->push_back(Sequence(seq), load_titles ? id.c_str() : nullptr, q, oid++, this->value_traits_.seq_type, frame_mask, !preserve_dna); + + ++seq_count; + if (seq_count <= CHECK_FOR_DNA_COUNT && value_traits_.seq_type == SequenceType::amino_acid && Util::Seq::looks_like_dna(Sequence(seq)) && !config.ignore_warnings) + throw std::runtime_error("The sequences are expected to be proteins but only contain DNA letters. Use the option --ignore-warnings to proceed."); + } while (letters < max_letters || seq_count % modulo != 0); + if (file_count() == 2 && !files_synced()) + throw std::runtime_error("Unequal number of sequences in paired read files."); + block->seqs_.finish_reserve(); + if (value_traits_.seq_type == SequenceType::nucleotide) + block->source_seqs_.finish_reserve(); + if (load_titles) + block->ids_.finish_reserve(); + if (q) + block->qual_.finish_reserve(); + return { block, seq_count }; +} + +size_t SequenceFile::dict_block(const size_t ref_block) +{ + return config.multiprocessing ? ref_block : 0; +} - if (blocked_processing) // should be always +Block* SequenceFile::load_seqs(const size_t max_letters, const BitVector* filter, LoadFlags flags, const Chunk& chunk) +{ + reopen(); + + if(max_letters == 0) + seek_chunk(chunk); + + Block* block; + int64_t seqs_processed; + if(flag_any(format_flags_, FormatFlags::LENGTH_LOOKUP)) + tie(block, seqs_processed) = load_twopass(max_letters, filter, flags, chunk); + else { + if (chunk.n_seqs) + throw OperationNotSupported(); + tie(block, seqs_processed) = load_onepass(max_letters, filter, flags); + } + + if (!flag_any(flags, LoadFlags::NO_CLOSE_WEAKLY)) close_weakly(); + if (block->empty()) + return block; + + if (flag_any(flags, LoadFlags::LAZY_MASKING)) + block->masked_.resize(block->seqs_.size(), false); - if (lazy_masking) - block->masked_.resize(filtered_seq_count, false); + if (flag_any(flags, LoadFlags::CONVERT_ALPHABET)) + block->seqs_.convert_all_to_std_alph(config.threads_); + block->seqs_.print_stats(); return block; } @@ -200,16 +294,29 @@ void SequenceFile::get_seq() vector seq; string id; - bool all = config.seq_no.size() == 0 && seq_titles.empty(); + bool all = config.seq_no.size() == 0 && seq_titles.empty() && config.oid_list.empty(); + std::set seqs; if (!all) for (vector::const_iterator i = config.seq_no.begin(); i != config.seq_no.end(); ++i) seqs.insert(atoi(i->c_str()) - 1); + if (!config.oid_list.empty()) { + TextInputFile f(config.oid_list); + OId oid; + while (f.getline(), !f.line.empty() || !f.eof()) { + Util::String::Tokenizer(f.line, "\t") >> oid; + seqs.insert(oid); + } + f.close(); + } + if (!seqs.empty()) + message_stream << "#Selected sequences: " << seqs.size() << endl; + const size_t max_letters = config.chunk_size == 0.0 ? std::numeric_limits::max() : (size_t)(config.chunk_size * 1e9); size_t letters = 0; TextBuffer buf; OutputFile out(config.output_file); - for (size_t n = 0; n < sequence_count(); ++n) { + for (int64_t n = 0; n < sequence_count(); ++n) { read_seq(seq, id); std::map::const_iterator mapped_title = seq_titles.find(Util::Seq::seqid(id.c_str(), false)); if (all || seqs.find(n) != seqs.end() || mapped_title != seq_titles.end()) { @@ -237,6 +344,18 @@ void SequenceFile::get_seq() out.close(); } +Util::Tsv::File* SequenceFile::make_seqid_list() { + Util::Tsv::File* f = new Util::Tsv::File(Util::Tsv::Schema{ Util::Tsv::Type::STRING }, "", Util::Tsv::Flags::TEMP); + vector seq; + string id; + init_seq_access(); + for (int64_t n = 0; n < sequence_count(); ++n) { + read_seq(seq, id); + f->write_record(Util::Seq::seqid(id.c_str(), false)); + } + return f; +} + SequenceFile::~SequenceFile() { if (dict_file_) { @@ -245,32 +364,38 @@ SequenceFile::~SequenceFile() } } -SequenceFile* SequenceFile::auto_create(string& path, Flags flags, Metadata metadata) { +static bool is_blast_db(const string& path) { if (exists(path + ".pin") || exists(path + ".pal")) { #ifdef WITH_BLASTDB if (config.multiprocessing) throw std::runtime_error("--multiprocessing is not compatible with BLAST databases."); if (config.target_indexed) throw std::runtime_error("--target-indexed is not compatible with BLAST databases."); - return new BlastDB(path, metadata, flags); + return true; #else throw std::runtime_error("This executable was not compiled with support for BLAST databases."); #endif } - path = auto_append_extension_if_exists(path, DatabaseFile::FILE_EXTENSION); - if (DatabaseFile::is_diamond_db(path)) { - return new DatabaseFile(path, metadata, flags); - } - else if (!flag_any(flags, Flags::NO_FASTA)) { - message_stream << "Database file is not a DIAMOND or BLAST database, treating as FASTA." << std::endl; - config.input_ref_file = { path }; - TempFile* db; - DatabaseFile::make_db(&db); - DatabaseFile* r(new DatabaseFile(*db)); - delete db; - return r; + return false; +} + +SequenceFile* SequenceFile::auto_create(const vector& path, Flags flags, Metadata metadata, const ValueTraits& value_traits) { + if (path.size() == 1) { + if (is_blast_db(path.front())) +#ifdef WITH_BLASTDB + return new BlastDB(path.front(), metadata, flags, value_traits); +#else + return nullptr; +#endif + const string a = auto_append_extension_if_exists(path.front(), DatabaseFile::FILE_EXTENSION); + if (DatabaseFile::is_diamond_db(a)) + return new DatabaseFile(a, metadata, flags, value_traits); + } + if (!flag_any(flags, Flags::NO_FASTA)) { + //message_stream << "Database file is not a DIAMOND or BLAST database, treating as FASTA." << std::endl; + return new FastaFile(path, metadata, flags, value_traits); } - throw std::runtime_error("Database does not have a supported format."); + throw std::runtime_error("Sequence file does not have a supported format."); } void SequenceFile::load_dict_block(InputFile* f, const size_t ref_block) @@ -284,12 +409,12 @@ void SequenceFile::load_dictionary(const size_t query_block, const size_t ref_bl return; task_timer timer("Loading dictionary", 3); if (config.multiprocessing) { - dict_oid_ = vector>(ref_blocks); + dict_oid_ = vector>(ref_blocks); if (flag_any(flags_, Flags::SELF_ALN_SCORES)) dict_self_aln_score_ = vector>(ref_blocks); reserve_dict(ref_blocks); for (size_t i = 0; i < ref_blocks; ++i) { - InputFile f(dict_file_name(query_block, i)); + InputFile f(dict_file_name(query_block, i), InputFile::NO_AUTODETECT); load_dict_block(&f, i); f.close_and_delete(); } @@ -307,7 +432,7 @@ void SequenceFile::load_dictionary(const size_t query_block, const size_t ref_bl reserve_dict(0); InputFile f(*t); load_dict_block(&f, 0); - if (dict_oid_.front().size() != next_dict_id_) + if ((DictId)dict_oid_.front().size() != next_dict_id_) throw std::runtime_error("Dictionary corrupted."); f.close_and_delete(); dict_file_.reset(); @@ -318,10 +443,19 @@ void SequenceFile::free_dictionary() { dict_oid_.clear(); dict_oid_.shrink_to_fit(); + dict_len_.clear(); + dict_len_.shrink_to_fit(); + dict_title_.clear(); + dict_title_.shrink_to_fit(); + dict_seq_.clear(); + dict_seq_.shrink_to_fit(); + dict_self_aln_score_.clear(); + dict_self_aln_score_.shrink_to_fit(); + block_to_dict_id_.clear(); } size_t SequenceFile::total_blocks() const { - const size_t c = config.chunk_size * 1e9; + const size_t c = (size_t)(config.chunk_size * 1e9); return (this->letters() + c - 1) / c; } @@ -378,7 +512,7 @@ void SequenceFile::init_dict_block(size_t block, size_t seq_count, bool persist) if(!persist) block_to_dict_id_.clear(); if(block_to_dict_id_.find(block) == block_to_dict_id_.end()) - block_to_dict_id_[block] = vector(seq_count, DICT_EMPTY); + block_to_dict_id_[block] = vector(seq_count, DICT_EMPTY); } void SequenceFile::close_dict_block(bool persist) @@ -391,13 +525,13 @@ void SequenceFile::close_dict_block(bool persist) block_to_dict_id_.clear(); } -uint32_t SequenceFile::dict_id(size_t block, size_t block_id, size_t oid, size_t len, const char* id, const Letter* seq, const double self_aln_score) +DictId SequenceFile::dict_id(size_t block, size_t block_id, size_t oid, size_t len, const char* id, const Letter* seq, const double self_aln_score) { auto it = block_to_dict_id_.find(block); if (it == block_to_dict_id_.end() || block_id >= it->second.size()) throw std::runtime_error("Dictionary not initialized."); - vector& v = it->second; - uint32_t n = v[block_id]; + vector& v = it->second; + DictId n = v[block_id]; if (n != DICT_EMPTY) return n; { @@ -412,10 +546,10 @@ uint32_t SequenceFile::dict_id(size_t block, size_t block_id, size_t oid, size_t } } -size_t SequenceFile::oid(uint32_t dict_id, const size_t ref_block) const +size_t SequenceFile::oid(DictId dict_id, const size_t ref_block) const { const size_t b = dict_block(ref_block); - if (b >= dict_oid_.size() || dict_id >= dict_oid_[b].size()) + if (b >= dict_oid_.size() || dict_id >= (DictId)dict_oid_[b].size()) throw std::runtime_error("Dictionary not loaded."); return dict_oid_[b][dict_id]; } @@ -428,16 +562,258 @@ double SequenceFile::dict_self_aln_score(const size_t dict_id, const size_t ref_ return dict_self_aln_score_[b][dict_id]; } -SequenceFile::SequenceFile(Type type, Alphabet alphabet, Flags flags): +SequenceFile::SequenceFile(Type type, Alphabet alphabet, Flags flags, FormatFlags format_flags, const ValueTraits& value_traits): flags_(flags), + format_flags_(format_flags), + value_traits_(value_traits), type_(type), - alphabet_(alphabet) -{} + alphabet_(alphabet) +{ + if (flag_any(flags_, Flags::OID_TO_ACC_MAPPING)) + //seqid_file_.reset(new File(Schema{ ::Type::INT64, ::Type::STRING }, "", ::Flags::TEMP)); // | ::Flags::RECORD_ID_COLUMN)); + seqid_file_.reset(new File(Schema{ ::Type::STRING }, "", ::Flags::TEMP)); +} + +void SequenceFile::write_dict_entry(size_t block, size_t oid, size_t len, const char* id, const Letter* seq, const double self_aln_score) +{ + OutputFile& f = *dict_file_; + f << (uint32_t)oid; + if(flag_any(format_flags_, FormatFlags::DICT_LENGTHS)) + f << (uint32_t)len; + if (flag_any(format_flags_, FormatFlags::DICT_SEQIDS)) { + f << id; + dict_alloc_size_ += strlen(id); + } + if (flag_any(flags_, Flags::TARGET_SEQS)) + f.write(seq, len); + if (flag_any(flags_, Flags::SELF_ALN_SCORES)) + f << self_aln_score; +} + +bool SequenceFile::load_dict_entry(InputFile& f, size_t ref_block) +{ + const size_t b = dict_block(ref_block); + uint32_t oid, len; + string title; + try { + f >> oid; + } + catch (EndOfStream&) { + return false; + } + dict_oid_[b].push_back(oid); + if (flag_any(format_flags_, FormatFlags::DICT_LENGTHS)) { + f >> len; + dict_len_[b].push_back(len); + } + if (flag_any(format_flags_, FormatFlags::DICT_SEQIDS)) { + f >> title; + dict_title_[b].push_back(title.begin(), title.end()); + } + if (flag_any(flags_, Flags::TARGET_SEQS)) { + vector v(len); + f.read(v.data(), len); + dict_seq_[b].push_back(v.begin(), v.end()); + } + if (flag_any(flags_, Flags::SELF_ALN_SCORES)) { + double self_aln_score; + f >> self_aln_score; + dict_self_aln_score_[b].push_back(self_aln_score); + } + return true; +} + +void SequenceFile::reserve_dict(const size_t ref_blocks) +{ + if (config.multiprocessing) { + if (flag_any(format_flags_, FormatFlags::DICT_LENGTHS)) + dict_len_ = std::vector>(ref_blocks); + if (flag_any(format_flags_, FormatFlags::DICT_SEQIDS)) + dict_title_ = std::vector(ref_blocks); + if (flag_any(flags_, Flags::TARGET_SEQS)) + dict_seq_ = std::vector(ref_blocks); + } + else { + if (flag_any(format_flags_, FormatFlags::DICT_LENGTHS)) { + dict_len_ = { {} }; + dict_len_[0].reserve(next_dict_id_); + } + if (flag_any(format_flags_, FormatFlags::DICT_SEQIDS)) { + dict_title_ = { {} }; + dict_title_[0].reserve(next_dict_id_, dict_alloc_size_); + } + if (flag_any(flags_, Flags::TARGET_SEQS)) { + dict_seq_ = { {} }; + dict_seq_[0].reserve(next_dict_id_, 0); + } + } +} + +std::string SequenceFile::dict_title(DictId dict_id, const size_t ref_block) const +{ + const size_t b = dict_block(ref_block); + if (b >= dict_title_.size() || dict_id >= (DictId)dict_title_[b].size()) + throw std::runtime_error("Dictionary not loaded."); + return dict_title_[b][dict_id]; +} + +Loc SequenceFile::dict_len(DictId dict_id, const size_t ref_block) const +{ + const size_t b = dict_block(ref_block); + if (b >= dict_len_.size() || dict_id >= (DictId)dict_len_[b].size()) + throw std::runtime_error("Dictionary not loaded."); + return dict_len_[b][dict_id]; +} + +std::vector SequenceFile::dict_seq(DictId dict_id, const size_t ref_block) const +{ + const size_t b = dict_block(ref_block); + if (b >= dict_seq_.size() || dict_id >= (DictId)dict_seq_[b].size()) + throw std::runtime_error("Dictionary not loaded."); + Sequence s = dict_seq_[b][dict_id]; + return vector(s.data(), s.end()); +} + +BitVector* SequenceFile::filter_by_taxonomy(const std::string& include, const std::string& exclude) const +{ + BitVector* v = new BitVector(sequence_count()); + if (!include.empty() && !exclude.empty()) + throw std::runtime_error("Options --taxonlist and --taxon-exclude are mutually exclusive."); + const bool e = !exclude.empty(); + const std::set taxon_filter_list(parse_csv(e ? exclude : include)); + if (taxon_filter_list.empty()) + throw std::runtime_error("Option --taxonlist/--taxon-exclude used with empty list."); + if (taxon_filter_list.find(1) != taxon_filter_list.end() || taxon_filter_list.find(0) != taxon_filter_list.end()) + throw std::runtime_error("Option --taxonlist/--taxon-exclude used with invalid argument (0 or 1)."); + for (OId i = 0; i < sequence_count(); ++i) + if (taxon_nodes_->contained(taxids(i), taxon_filter_list) ^ e) + v->set(i); + return v; +} + +void SequenceFile::build_acc_to_oid() { + acc2oid_.reserve(sequence_count()); + set_seqinfo_ptr(0); + vector seq; + string id; + for (OId i = 0; i < sequence_count(); ++i) { + read_seq(seq, id); + acc2oid_[Util::Seq::seqid(id.c_str(), false)] = i; + } +} + +vector SequenceFile::accession_to_oid(const string& accession) const { + try { + return { acc2oid_.at(accession) }; + } + catch (std::out_of_range&) { + throw runtime_error("Accession not found in database: " + accession); + } +} + +std::string SequenceFile::seqid(OId oid) const { + throw std::runtime_error("seqid"); + //if (oid >= acc_.size()) + //throw std::runtime_error("OId to accession mapping not available."); + //return acc_[oid]; +} + +void SequenceFile::write_accession_list(const std::vector& oids, std::string& file_name) { + ofstream f(file_name); + const Util::Tsv::Table acc = seqid_file().read(config.threads_); + for (OId i = 0; i < sequence_count(); ++i) + if (!oids[i]) + f << acc[i].get(0) << endl; +} + +template +std::vector SequenceFile::seq_offsets(It begin, It end) { + assert(std::is_sorted(begin, end)); + vector r; + if (end <= begin) + return r; + r.reserve(end - begin); + set_seqinfo_ptr(0); + init_seqinfo_access(); + const OId end_oid = *(end - 1) + 1; + if (end_oid > sequence_count()) + throw runtime_error("OId out of bounds."); + It it = begin; + for (OId i = 0; i < end_oid; ++i) { + SeqInfo info = read_seqinfo(); + if (i == *it) { + if (it != begin && i - 1 == *(it - 1)) + r.push_back(-1); + else + r.push_back(info.pos); + ++it; + } + } + return r; +} + +template +void SequenceFile::sub_db(It begin, It end, FastaFile* out) { + vector seq; + string id; + out->init_write(); + if (end <= begin) + return; + if (flag_any(format_flags_, FormatFlags::LENGTH_LOOKUP)) { + const vector pos = seq_offsets(begin, end); + for (int64_t p : pos) { + if (p >= 0) + seek_offset(p); + read_seq(seq, id); + out->write_seq(Sequence(seq), id); + } + } + else { + assert(std::is_sorted(begin, end)); + set_seqinfo_ptr(0); + for (OId i = 0; i <= *(end - 1); ++i) { + read_seq(seq, id); + if (*begin == i) { + out->write_seq(Sequence(seq), id); + ++begin; + } + } + } +} + +template void SequenceFile::sub_db::const_iterator>(vector::const_iterator, vector::const_iterator, FastaFile*); + +template +FastaFile* SequenceFile::sub_db(It begin, It end, const string& file_name) { + FastaFile* f = new FastaFile(file_name, true, FastaFile::WriteAccess()); + sub_db(begin, end, f); + return f; +} + +template FastaFile* SequenceFile::sub_db::const_iterator>(vector::const_iterator, vector::const_iterator, const string&); +template FastaFile* SequenceFile::sub_db::const_iterator>(vector::const_iterator, vector::const_iterator, const string&); + +pair SequenceFile::read_fai_file(const string& file_name, int64_t seqs, int64_t letters) { + string acc; + Loc len; + TextInputFile fai(file_name); + while (fai.getline(), !fai.line.empty() || !fai.eof()) { + Util::String::Tokenizer(fai.line, "\t") >> acc >> len; + if (flag_any(flags_, Flags::ACC_TO_OID_MAPPING)) + acc2oid_[acc] = seqs; + //if (flag_any(flags_, Flags::OID_TO_ACC_MAPPING)) +// acc_.push_back(acc.begin(), acc.end()); + ++seqs; + letters += len; + } + fai.close(); + return { seqs, letters }; +} void db_info() { if (config.database.empty()) throw std::runtime_error("Missing option for database file: --db/-d."); - SequenceFile* db = SequenceFile::auto_create(config.database, SequenceFile::Flags::NO_FASTA | SequenceFile::Flags::NO_COMPATIBILITY_CHECK); + SequenceFile* db = SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NO_FASTA | SequenceFile::Flags::NO_COMPATIBILITY_CHECK); const std::streamsize w = 25; cout << setw(w) << "Database type " << to_string(db->type()) << endl; cout << setw(w) << "Database format version " << db->db_version() << endl; @@ -449,4 +825,90 @@ void db_info() { cout << setw(w) << "Letters " << db->letters() << endl; db->close(); delete db; +} + +void prep_db() { + config.database.require(); + if (is_blast_db(config.database)) +#ifdef WITH_BLASTDB + BlastDB::prep_blast_db(config.database); +#else + ; +#endif +#ifdef EXTRA + else if (DatabaseFile::is_diamond_db(auto_append_extension_if_exists(config.database, DatabaseFile::FILE_EXTENSION))) + DatabaseFile::prep_db(); + else + FastaFile::prep_db(config.database); +#else + throw runtime_error("Database file is not a BLAST database"); +#endif +} + +void SequenceFile::add_seqid_mapping(const std::string& id, OId oid) { + const string acc = Util::Seq::seqid(id.c_str(), false); + if (flag_any(flags_, Flags::ACC_TO_OID_MAPPING)) { + if (oid != (OId)acc2oid_.size()) + throw runtime_error("add_seqid_mapping"); + auto r = acc2oid_.emplace(acc, oid); + if (!r.second) + throw runtime_error("Accession is not unique in database file: " + acc); + } + if (flag_any(flags_, Flags::OID_TO_ACC_MAPPING)) { + seqid_file_->write_record(acc); + } +} + +vector, File*>> SequenceFile::length_sort(int64_t block_size) { + vector, File*>> files; + init_seq_access(); + vector seq; + string id; + vector> lengths; + lengths.reserve(sequence_count()); + for (OId i = 0; i < sequence_count(); ++i) { + read_seq(seq, id); + lengths.emplace_back((Loc)seq.size(), i); + } + ips4o::parallel::sort(lengths.begin(), lengths.end(), greater>(), config.threads_); + + int64_t letters = 0, seqs = 0; + int block = 0; + for (auto i = lengths.begin(); i != lengths.end(); ++i) { + letters += i->first; + ++seqs; + i->first = block; + if (letters >= block_size || seqs >= numeric_limits::max()) { + ++block; + letters = 0; + seqs = 0; + } + } + if (letters > 0) ++block; + ips4o::parallel::sort(lengths.begin(), lengths.end(), [](const pair& p1, const pair& p2) { return p1.second < p2.second; }, config.threads_); + + files.reserve(block); + for (int i = 0; i < block; ++i) { + //files.emplace_back(new FastaFile("", true, FastaFile::WriteAccess()), vector(), + files.emplace_back(new FastaFile("block_" + std::to_string(i), true, FastaFile::WriteAccess()), vector(), + new File(Schema{ ::Type::INT64 }, "", ::Flags::TEMP)); + get<0>(files.back())->init_write(); + } + init_seq_access(); + //Util::Tsv::File map_file(Util::Tsv::Schema{ Util::Tsv::Type::INT64 }, "", Util::Tsv::Flags::WRITE_ACCESS); + for (OId i = 0; i < sequence_count(); ++i) { + read_seq(seq, id); + auto& f = files[lengths[i].first]; + get<0>(f)->write_seq(seq, id); + get<2>(f)->write_record(i); + + } + for (auto f : files) + get<0>(f)->set_seqinfo_ptr(0); + return files; +} + +Util::Tsv::File& SequenceFile::seqid_file() { + seqid_file_->rewind(); + return *seqid_file_; } \ No newline at end of file diff --git a/src/data/sequence_file.h b/src/data/sequence_file.h index e982dbe6d..986f8e110 100644 --- a/src/data/sequence_file.h +++ b/src/data/sequence_file.h @@ -20,6 +20,8 @@ along with this program. If not, see . #pragma once +#include +#include #include "../util/io/input_file.h" #include "sequence_set.h" #include "../util/data_structures/bit_vector.h" @@ -27,66 +29,93 @@ along with this program. If not, see . #include "taxonomy_nodes.h" #include "../util/enum.h" #include "../util/data_structures/bit_vector.h" -#include "block.h" +#include "block/block.h" +#include "../util/tsv/file.h" struct Chunk { Chunk() : i(0), offset(0), n_seqs(0) {} - Chunk(size_t i_, size_t offset_, size_t n_seqs_) : i(i_), offset(offset_), n_seqs(n_seqs_) + Chunk(int i_, size_t offset_, size_t n_seqs_) : i(i_), offset(offset_), n_seqs(n_seqs_) {} - size_t i; + int i; size_t offset; - size_t n_seqs; + int64_t n_seqs; }; -struct SeqInfo -{ - SeqInfo() - {} - SeqInfo(uint64_t pos, size_t len) : - pos(pos), - seq_len(uint32_t(len)) - {} - uint64_t pos; - uint32_t seq_len; - enum { SIZE = 16 }; +struct AccessionNotFound : public std::exception { }; -struct AccessionNotFound : public std::exception { +struct OperationNotSupported : public std::exception { }; +struct FastaFile; + struct SequenceFile { - enum class Type { DMND = 0, BLAST = 1 }; + enum class Type { DMND = 0, BLAST = 1, FASTA = 2, BLOCK = 3 }; enum class Metadata : int { - TAXON_MAPPING = 1, - TAXON_NODES = 1 << 1, + TAXON_MAPPING = 1, + TAXON_NODES = 1 << 1, TAXON_SCIENTIFIC_NAMES = 1 << 2, - TAXON_RANKS = 1 << 3 + TAXON_RANKS = 1 << 3 }; enum class Flags : int { - NONE = 0, + NONE = 0, NO_COMPATIBILITY_CHECK = 1, - NO_FASTA = 1 << 1, - ALL_SEQIDS = 1 << 2, - FULL_TITLES = 1 << 3, - TARGET_SEQS = 1 << 4, - SELF_ALN_SCORES = 1 << 5 + NO_FASTA = 1 << 1, + ALL_SEQIDS = 1 << 2, + FULL_TITLES = 1 << 3, + TARGET_SEQS = 1 << 4, + SELF_ALN_SCORES = 1 << 5, + NEED_LETTER_COUNT = 1 << 6, + ACC_TO_OID_MAPPING = 1 << 7, + OID_TO_ACC_MAPPING = 1 << 8 }; - enum class LoadTitles { - SINGLE_PASS, LAZY + enum class FormatFlags { + TITLES_LAZY = 1, + DICT_LENGTHS = 1 << 1, + DICT_SEQIDS = 1 << 2, + LENGTH_LOOKUP = 1 << 3, + SEEKABLE = 1 << 4 }; - SequenceFile(Type type, Alphabet alphabet, Flags flags); + enum class LoadFlags { + SEQS = 1, + TITLES = 1 << 1, + QUALITY = 1 << 2, + LAZY_MASKING = 1 << 3, + CONVERT_ALPHABET = 1 << 4, + NO_CLOSE_WEAKLY = 1 << 5, + DNA_PRESERVATION = 1 << 6, + ALL = SEQS | TITLES + }; + struct SeqInfo + { + SeqInfo() + {} + SeqInfo(uint64_t pos, size_t len) : + pos(pos), + seq_len(uint32_t(len)) + {} + uint64_t pos; + uint32_t seq_len; + enum { SIZE = 16 }; + }; + + SequenceFile(Type type, Alphabet alphabet, Flags flags, FormatFlags format_flags, const ValueTraits& value_traits = amino_acid_traits); + + virtual int64_t file_count() const = 0; virtual void init_seqinfo_access() = 0; virtual void init_seq_access() = 0; virtual void seek_chunk(const Chunk& chunk) = 0; - virtual size_t tell_seq() const = 0; + virtual OId tell_seq() const = 0; + virtual bool eof() const = 0; + virtual bool files_synced(); virtual SeqInfo read_seqinfo() = 0; virtual void putback_seqinfo() = 0; virtual size_t id_len(const SeqInfo& seq_info, const SeqInfo& seq_info_next) = 0; @@ -94,58 +123,62 @@ struct SequenceFile { virtual void read_seq_data(Letter* dst, size_t len, size_t& pos, bool seek) = 0; virtual void read_id_data(const int64_t oid, char* dst, size_t len) = 0; virtual void skip_id_data() = 0; - virtual std::string seqid(size_t oid) const = 0; - virtual std::string dict_title(size_t dict_id, const size_t ref_block) const = 0; - virtual size_t dict_len(size_t dict_id, const size_t ref_block) const = 0; - virtual std::vector dict_seq(size_t dict_id, const size_t ref_block) const = 0; - virtual size_t sequence_count() const = 0; - virtual size_t sparse_sequence_count() const = 0; + virtual std::string seqid(OId oid) const; + virtual std::string dict_title(DictId dict_id, const size_t ref_block) const; + virtual Loc dict_len(DictId dict_id, const size_t ref_block) const; + virtual std::vector dict_seq(DictId dict_id, const size_t ref_block) const; + virtual int64_t sequence_count() const = 0; + virtual int64_t sparse_sequence_count() const = 0; virtual size_t letters() const = 0; virtual int db_version() const = 0; virtual int program_build_version() const = 0; - virtual void read_seq(std::vector& seq, std::string& id) = 0; + virtual bool read_seq(std::vector& seq, std::string& id, std::vector* quals = nullptr) = 0; virtual Metadata metadata() const = 0; - virtual TaxonomyNodes* taxon_nodes() = 0; - virtual std::vector* taxon_scientific_names() = 0; + virtual std::string taxon_scientific_name(TaxId taxid) const; virtual int build_version() = 0; virtual void create_partition_balanced(size_t max_letters) = 0; virtual void save_partition(const std::string& partition_file_name, const std::string& annotation = "") = 0; - virtual size_t get_n_partition_chunks() = 0; - virtual void set_seqinfo_ptr(size_t i) = 0; + virtual int get_n_partition_chunks() = 0; + virtual void set_seqinfo_ptr(OId i) = 0; virtual void close() = 0; virtual void close_weakly() = 0; virtual void reopen() = 0; virtual BitVector* filter_by_accession(const std::string& file_name) = 0; - virtual BitVector* filter_by_taxonomy(const std::string& include, const std::string& exclude, TaxonomyNodes& nodes) = 0; - virtual std::vector taxids(size_t oid) const = 0; + virtual std::vector taxids(size_t oid) const = 0; virtual const BitVector* builtin_filter() = 0; virtual std::string file_name() = 0; virtual void seq_data(size_t oid, std::vector& dst) const = 0; virtual size_t seq_length(size_t oid) const = 0; virtual void init_random_access(const size_t query_block, const size_t ref_blocks, bool dictionary = true) = 0; virtual void end_random_access(bool dictionary = true) = 0; - virtual std::vector accession_to_oid(const std::string& acc) const = 0; - virtual LoadTitles load_titles() = 0; + virtual std::vector accession_to_oid(const std::string& acc) const; + virtual void init_write(); + virtual void write_seq(const Sequence& seq, const std::string& id); virtual ~SequenceFile(); Type type() const { return type_; } - Block* load_seqs( - const size_t max_letters, - bool load_ids = true, - const BitVector* filter = nullptr, - bool fetch_seqs = true, - bool lazy_masking = false, - const Chunk& chunk = Chunk()); + Block* load_seqs(const size_t max_letters, const BitVector* filter = nullptr, LoadFlags flags = LoadFlags(3), const Chunk& chunk = Chunk()); void get_seq(); + Util::Tsv::File* make_seqid_list(); size_t total_blocks() const; SequenceSet seqs_by_accession(const std::vector::const_iterator begin, const std::vector::const_iterator end) const; std::vector seq_by_accession(const std::string& acc) const; + BitVector* filter_by_taxonomy(const std::string& include, const std::string& exclude) const; + void write_accession_list(const std::vector& oids, std::string& file_name); + template + std::vector seq_offsets(It begin, It end); + template + FastaFile* sub_db(It begin, It end, const std::string& file_name = std::string()); + template + void sub_db(It begin, It end, FastaFile* out); + std::vector, Util::Tsv::File*>> length_sort(int64_t block_size); + Util::Tsv::File& seqid_file(); void init_dict(const size_t query_block, const size_t target_block); void init_dict_block(size_t block, size_t seq_count, bool persist); void close_dict_block(bool persist); - uint32_t dict_id(size_t block, size_t block_id, size_t oid, size_t len, const char* id, const Letter* seq, const double self_aln_score); - size_t oid(uint32_t dict_id, const size_t ref_block) const; + DictId dict_id(size_t block, size_t block_id, size_t oid, size_t len, const char* id, const Letter* seq, const double self_aln_score); + size_t oid(DictId dict_id, const size_t ref_block) const; double dict_self_aln_score(const size_t dict_size, const size_t ref_block) const; size_t dict_size() const { return next_dict_id_; @@ -153,36 +186,74 @@ struct SequenceFile { Flags flags() const { return flags_; } + FormatFlags format_flags() const { + return format_flags_; + } + const TaxonomyNodes& taxon_nodes() const { + return *taxon_nodes_; + } - static SequenceFile* auto_create(string& path, Flags flags = Flags::NONE, Metadata metadata = Metadata()); + static SequenceFile* auto_create(const std::vector& path, Flags flags = Flags::NONE, Metadata metadata = Metadata(), const ValueTraits& value_traits = amino_acid_traits); + + size_t mem_size() const { + size_t n = 0; + for (auto& v : dict_oid_) + n += v.size() * sizeof(OId); + for (auto& v : dict_len_) + n += v.size() * sizeof(uint32_t); + for (auto& v : dict_title_) + n += v.raw_len(); + for (auto& v : dict_seq_) + n += v.raw_len(); + for (auto& v : dict_self_aln_score_) + n += v.size() * sizeof(double); + if (!acc2oid_.empty() || !block_to_dict_id_.empty()) + std::terminate(); + return n; + } protected: + static const char* const SEQID_HDR; + void load_dictionary(const size_t query_block, const size_t ref_blocks); void free_dictionary(); static size_t dict_block(const size_t ref_block); + void build_acc_to_oid(); + std::pair read_fai_file(const std::string& file_name, int64_t seqs, int64_t letters); + void add_seqid_mapping(const std::string& id, OId oid); const Flags flags_; + const FormatFlags format_flags_; + const ValueTraits& value_traits_; std::unique_ptr dict_file_; - uint32_t next_dict_id_; + DictId next_dict_id_; size_t dict_alloc_size_; - std::vector> dict_oid_; + std::vector> dict_oid_; + std::vector> dict_len_; + std::vector dict_title_; + std::vector dict_seq_; std::vector> dict_self_aln_score_; + std::unique_ptr taxon_nodes_; + std::unordered_map acc2oid_; + std::unique_ptr seqid_file_; + StringSet acc_; private: - enum { DICT_EMPTY = UINT32_MAX }; + static const DictId DICT_EMPTY; - virtual void write_dict_entry(size_t block, size_t oid, size_t len, const char* id, const Letter* seq, const double self_aln_score) = 0; - virtual bool load_dict_entry(InputFile& f, const size_t ref_block) = 0; - virtual void reserve_dict(const size_t ref_blocks) = 0; - void load_block(size_t block_id_begin, size_t block_id_end, size_t pos, bool use_filter, const vector* filtered_pos, bool load_ids, Block* block); + void write_dict_entry(size_t block, size_t oid, size_t len, const char* id, const Letter* seq, const double self_aln_score); + bool load_dict_entry(InputFile& f, const size_t ref_block); + void reserve_dict(const size_t ref_blocks); + std::pair load_twopass(const int64_t max_letters, const BitVector* filter, LoadFlags flags, const Chunk& chunk); + std::pair load_onepass(const int64_t max_letters, const BitVector* filter, LoadFlags flags); void load_dict_block(InputFile* f, const size_t ref_block); const Type type_; const Alphabet alphabet_; - std::map> block_to_dict_id_; + std::map> block_to_dict_id_; std::mutex dict_mtx_; }; @@ -191,5 +262,7 @@ template<> struct EnumTraits { static const EMap to_string; }; -DEF_ENUM_FLAG_OPERATORS(SequenceFile::Flags) -DEF_ENUM_FLAG_OPERATORS(SequenceFile::Metadata) \ No newline at end of file +DEFINE_ENUM_FLAG_OPERATORS(SequenceFile::Flags) +DEFINE_ENUM_FLAG_OPERATORS(SequenceFile::Metadata) +DEFINE_ENUM_FLAG_OPERATORS(SequenceFile::FormatFlags) +DEFINE_ENUM_FLAG_OPERATORS(SequenceFile::LoadFlags) diff --git a/src/data/sequence_set.cpp b/src/data/sequence_set.cpp index efb62ef03..41e57dc61 100644 --- a/src/data/sequence_set.cpp +++ b/src/data/sequence_set.cpp @@ -27,6 +27,10 @@ along with this program. If not, see . #include "../util/sequence/sequence.h" #include "../util/log_stream.h" +using std::vector; +using std::pair; +using std::string; + SequenceSet::SequenceSet(Alphabet alphabet) : alphabet_(alphabet) { } @@ -36,20 +40,20 @@ void SequenceSet::print_stats() const verbose_stream << "Sequences = " << this->size() << ", letters = " << this->letters() << ", average length = " << this->avg_len() << std::endl; } -std::pair SequenceSet::len_bounds(size_t min_len) const +std::pair SequenceSet::len_bounds(Length min_len) const { const size_t l(this->size()); - size_t max = 0, min = std::numeric_limits::max(); + Length max = 0, min = std::numeric_limits::max(); for (size_t i = 0; i < l; ++i) { max = std::max(this->length(i), max); min = this->length(i) >= min_len ? std::min(this->length(i), min) : min; } - return std::pair(min, max); + return std::pair(min, max); } -size_t SequenceSet::max_len(size_t begin, size_t end) const +SequenceSet::Length SequenceSet::max_len(size_t begin, size_t end) const { - size_t max = 0; + Length max = 0; for (size_t i = begin; i < end; ++i) max = std::max(this->length(i), max); return max; @@ -60,7 +64,7 @@ std::vector SequenceSet::partition(unsigned n_part) const std::vector v; const size_t l = (this->letters() + n_part - 1) / n_part; v.push_back(0); - for (unsigned i = 0; i < this->size();) { + for (Id i = 0; i < this->size();) { size_t n = 0; while (i < this->size() && n < l) n += this->length(i++); @@ -74,7 +78,7 @@ std::vector SequenceSet::partition(unsigned n_part) const size_t SequenceSet::reverse_translated_len(size_t i) const { const size_t j(i - i % 6); - const size_t l(this->length(j)); + const Loc l(this->length(j)); if (this->length(j + 2) == l) return l * 3 + 2; else if (this->length(j + 1) == l) @@ -129,7 +133,7 @@ void SequenceSet::convert_all_to_std_alph(size_t threads) size_t max_id_len(const StringSet& ids) { size_t max(0); - for (size_t i = 0; i < ids.size(); ++i) + for (BlockId i = 0; i < ids.size(); ++i) max = std::max(max, find_first_of(ids[i], Util::Seq::id_delimiters)); return max; } @@ -137,4 +141,12 @@ size_t max_id_len(const StringSet& ids) vector seq_titles(const char* title) { return tokenize(title, "\1"); +} + +std::vector> SequenceSet::lengths() const { + vector> l; + l.reserve(size()); + for (BlockId i = 0; i < size(); ++i) + l.emplace_back(length(i), i); + return l; } \ No newline at end of file diff --git a/src/data/sequence_set.h b/src/data/sequence_set.h index 48e2df0fe..66e1a1c85 100644 --- a/src/data/sequence_set.h +++ b/src/data/sequence_set.h @@ -30,6 +30,10 @@ struct SequenceSet : public StringSetBase { SequenceSet(Alphabet alphabet = Alphabet::STD); + SequenceSet(StringSetBase&& string_set): + StringSetBase(string_set), + alphabet_(Alphabet::STD) + {} void print_stats() const; @@ -38,9 +42,9 @@ struct SequenceSet : public StringSetBase return Sequence(ptr(i), (Loc)length(i)); } - std::pair len_bounds(size_t min_len) const; + std::pair len_bounds(Length min_len) const; - size_t max_len(size_t begin, size_t end) const; + Length max_len(size_t begin, size_t end) const; std::vector partition(unsigned n_part) const; @@ -62,6 +66,7 @@ struct SequenceSet : public StringSetBase void convert_to_std_alph(size_t id); void convert_all_to_std_alph(size_t threads); + std::vector> lengths() const; private: diff --git a/src/data/string_set.h b/src/data/string_set.h index 73762c880..52b956fa7 100644 --- a/src/data/string_set.h +++ b/src/data/string_set.h @@ -31,6 +31,10 @@ template struct StringSetBase { + using Length = Loc; + using Id = BlockId; + using Pos = int64_t; + enum { PERIMETER_PADDING = 256 }; static const char DELIMITER = _pchar; @@ -75,9 +79,16 @@ struct StringSetBase data_.insert(data_.end(), _padding, _pchar); } + void append(const StringSetBase& s) { + reserve(size() + s.size(), letters() + s.letters()); + for (Id i = 0; i < s.size(); ++i) + push_back(s.ptr(i), s.end(i)); + } + template void assign(const size_t i, const It begin, const It end) { std::copy(begin, end, ptr(i)); + std::fill(ptr(i) + (end - begin), ptr(i) + (end - begin) + _padding, _pchar); } void fill(size_t n, T v) @@ -104,20 +115,26 @@ struct StringSetBase return i; } - size_t length(size_t i) const - { return limits_[i+1] - limits_[i] - _padding; } + Length length(size_t i) const + { + return Length(limits_[i + 1] - limits_[i] - _padding); + } - size_t size() const - { return limits_.size() - 1; } + Id size() const + { return Id(limits_.size() - 1); } bool empty() const { return limits_.size() <= 1; } - size_t raw_len() const + int64_t raw_len() const { return limits_.back(); } - size_t letters() const + int64_t mem_size() const { + return data_.size() * sizeof(T) + limits_.size() * sizeof(int64_t); + } + + int64_t letters() const { return raw_len() - size() - PERIMETER_PADDING; } T* data(uint64_t p = 0) @@ -129,13 +146,13 @@ struct StringSetBase size_t position(const T* p) const { return p - data(); } - size_t position(size_t i, size_t j) const + Pos position(Id i, Length j) const { return limits_[i] + j; } - std::pair local_position(size_t p) const + std::pair local_position(int64_t p) const { - size_t i = std::upper_bound(limits_.begin(), limits_.end(), p) - limits_.begin() - 1; - return std::pair(i, p - limits_[i]); + auto i = std::upper_bound(limits_.begin(), limits_.end(), p) - limits_.begin() - 1; + return std::pair(Id(i), Length(p - limits_[i])); } template @@ -152,17 +169,17 @@ struct StringSetBase return ptr(limits_.size() - 2); } - typename std::vector::const_iterator limits_begin() const { + typename std::vector::const_iterator limits_begin() const { return limits_.begin(); } - typename std::vector::const_iterator limits_end() const { + typename std::vector::const_iterator limits_end() const { return limits_.end(); } struct ConstIterator { - ConstIterator(const T* data, const size_t* limits): + ConstIterator(const T* data, const int64_t* limits): data_(data), limits_(limits) {} @@ -181,6 +198,10 @@ struct StringSetBase return limits_ == it.limits_; } + bool operator!=(const ConstIterator& it) const { + return limits_ != it.limits_; + } + ConstIterator operator+(ptrdiff_t d) const { return { data_ + *(limits_ + d) - *limits_, limits_ + d }; } @@ -195,14 +216,23 @@ struct StringSetBase return *this; } + ConstIterator& operator++() { + this->operator+=(1); + return *this; + } + std::pair operator[](const ptrdiff_t i) const { return { data_ + limits_[i] - limits_[0], limits_[i + 1] - limits_[i] - _padding }; } + std::pair operator*() const { + return this->operator[](0); + } + private: const T* data_; - const size_t* limits_; + const int64_t* limits_; }; @@ -214,10 +244,24 @@ struct StringSetBase return ConstIterator(nullptr, &limits_[size()]); } + template + StringSetBase subset(It begin, It end) const { + StringSetBase r; + r.limits_.reserve(end - begin); + for (It i = begin; i != end; ++i) + r.reserve(length(*i)); + r.finish_reserve(); + Id n = 0; + for (It i = begin; i != end; ++i, ++n) { + r.assign(n, ptr(*i), this->end(*i)); + } + return r; + } + private: std::vector data_; - std::vector limits_; + std::vector limits_; }; diff --git a/src/data/taxon_list.cpp b/src/data/taxon_list.cpp index 3fb07d4fa..78a16fdad 100644 --- a/src/data/taxon_list.cpp +++ b/src/data/taxon_list.cpp @@ -33,9 +33,12 @@ along with this program. If not, see . using std::set; using std::endl; using std::make_pair; +using std::vector; +using std::pair; +using std::string; TaxonList::TaxonList(Deserializer &in, size_t size, size_t data_size): - CompactArray>(in, size, data_size) + CompactArray>(in, size, data_size) {} static int mapping_file_format(const string& header) { @@ -52,9 +55,9 @@ static int mapping_file_format(const string& header) { throw std::runtime_error("Accession mapping file header has to be in one of these formats:\naccession\taccession.version\ttaxid\tgi\naccession.version\ttaxid"); } -static void load_mapping_file(ExternalSorter>& sorter) +static void load_mapping_file(ExternalSorter>& sorter) { - unsigned taxid; + TaxId taxid; TextInputFile f(config.prot_accession2taxid); f.getline(); int format = mapping_file_format(f.line); @@ -78,29 +81,27 @@ static void load_mapping_file(ExternalSorter>& sorter) accession.erase(i); if (accession != last) - sorter.push(make_pair(accession, (uint32_t)taxid)); + sorter.push(make_pair(accession, taxid)); last = accession; } f.close(); } -void TaxonList::build(OutputFile &db, ExternalSorter>& acc2oid, size_t seqs, Table& stats) +void TaxonList::build(OutputFile &db, ExternalSorter>& acc2oid, OId seqs, Util::Table& stats) { - typedef pair T; - task_timer timer("Loading taxonomy mapping file"); - ExternalSorter acc2taxid; + ExternalSorter> acc2taxid; load_mapping_file(acc2taxid); timer.go("Joining accession mapping"); acc2taxid.init_read(); acc2oid.init_read(); - const auto cmp = [](const T& x, const T& y) { return x.first < y.first; }; - const auto value = [](const T& x, const T& y) { return make_pair(x.second, y.second); }; - auto it = join_sorted_lists(acc2oid, acc2taxid, cmp, value); + const auto cmp = [](const pair& x, const pair& y) { return x.first < y.first; }; + const auto value = [](const pair& x, const pair& y) { return make_pair(x.second, y.second); }; + auto it = join_sorted_lists(acc2oid, acc2taxid, First(), First(), value); - ExternalSorter> oid2taxid; + ExternalSorter> oid2taxid; size_t acc_matched = 0; while (it.good()) { oid2taxid.push(*it); @@ -111,11 +112,10 @@ void TaxonList::build(OutputFile &db, ExternalSorter>& ac timer.go("Writing taxon id list"); db.set(Serializer::VARINT); oid2taxid.init_read(); - const uint32_t n = (uint32_t)seqs; - auto taxid_it = merge_keys(oid2taxid, First(), Second(), 0); + auto taxid_it = merge_keys(oid2taxid, First(), Second(), 0); size_t mapped_seqs = 0; - while (taxid_it.key() < n) { - set tax_ids = *taxid_it; + while (taxid_it.key() < seqs) { + set tax_ids = *taxid_it; tax_ids.erase(0); db << tax_ids; ++taxid_it; diff --git a/src/data/taxon_list.h b/src/data/taxon_list.h index 7373ed87c..fbd1340f5 100644 --- a/src/data/taxon_list.h +++ b/src/data/taxon_list.h @@ -23,13 +23,14 @@ along with this program. If not, see . #include "../util/io/output_file.h" #include "../util/data_structures/compact_array.h" #include "../util/table.h" +#include "../basic/value.h" template struct ExternalSorter; -struct TaxonList : public CompactArray> +struct TaxonList : public CompactArray> { - typedef std::pair T; + typedef std::pair T; TaxonList(Deserializer &in, size_t size, size_t data_size); - static void build(OutputFile &db, ExternalSorter>& accessions, size_t seqs, Table& stats); + static void build(OutputFile &db, ExternalSorter>& accessions, OId seqs, Util::Table& stats); }; \ No newline at end of file diff --git a/src/data/taxonomy.cpp b/src/data/taxonomy.cpp index f9368d44a..df3711fd9 100644 --- a/src/data/taxonomy.cpp +++ b/src/data/taxonomy.cpp @@ -36,6 +36,7 @@ using std::string; using std::map; using std::endl; using std::set; +using std::vector; const char* Rank::names[] = { "no rank", "superkingdom", "kingdom", "subkingdom", "superphylum", "phylum", "subphylum", "superclass", "class", "subclass", "infraclass", "cohort", "subcohort", "superorder", @@ -82,25 +83,10 @@ string get_accession(const string &title) return t; } -void Taxonomy::load_nodes() -{ - TextInputFile f(config.nodesdmp); - unsigned taxid, parent; - string rank; - while (!f.eof() && (f.getline(), !f.line.empty())) { - Util::String::Tokenizer(f.line, "\t|\t") >> taxid >> parent >> rank; - parent_.resize(taxid + 1); - parent_[taxid] = parent; - rank_.resize(taxid + 1); - rank_[taxid] = Rank(rank.c_str()); - } - f.close(); -} - size_t Taxonomy::load_names() { TextInputFile in(config.namesdmp); string name, type; - long id; + int64_t id; size_t n = 0; while (in.getline(), !in.eof()) { if (in.line.empty()) @@ -120,11 +106,6 @@ size_t Taxonomy::load_names() { void Taxonomy::init() { task_timer timer; - if (!config.nodesdmp.empty()) { - timer.go("Loading taxonomy nodes"); - load_nodes(); - timer.finish(); - } if (!config.namesdmp.empty()) { timer.go("Loading taxonomy names"); size_t n = load_names(); @@ -140,35 +121,3 @@ vector accession_from_title(const char *title) *i = get_accession(Util::Seq::seqid(i->c_str(), false)); return t; } - -unsigned Taxonomy::get_lca(unsigned t1, unsigned t2) const -{ - static const int max = 64; - if (t1 == t2 || t2 == 0) - return t1; - if (t1 == 0) - return t2; - unsigned p = t2; - set l; - int n = 0; - do { - p = get_parent(p); - if (p == 0) - return t1; - l.insert(p); - if (++n > max) - throw std::runtime_error("Path in taxonomy too long (1)."); - } while (p != t1 && p != 1); - if (p == t1) - return p; - p = t1; - n = 0; - while (l.find(p) == l.end()) { - p = get_parent(p); - if (p == 0) - return t2; - if (++n > max) - throw std::runtime_error("Path in taxonomy too long (2)."); - } - return p; -} \ No newline at end of file diff --git a/src/data/taxonomy.h b/src/data/taxonomy.h index c1f699f88..10cbc1540 100644 --- a/src/data/taxonomy.h +++ b/src/data/taxonomy.h @@ -31,27 +31,15 @@ along with this program. If not, see . #include "taxonomy_nodes.h" #include "../util/data_structures/bit_vector.h" -std::string get_accession(const string &t); +std::string get_accession(const std::string &t); std::vector accession_from_title(const char *title); struct Taxonomy { void init(); - void load_nodes(); size_t load_names(); - - unsigned get_parent(unsigned taxid) const - { - if (taxid >= parent_.size()) - throw std::runtime_error(std::string("No taxonomy node found for taxon id ") + std::to_string(taxid)); - return parent_[taxid]; - } - - unsigned get_lca(unsigned t1, unsigned t2) const; - std::vector parent_; std::vector name_; - std::vector rank_; friend struct TaxonomyNodes; diff --git a/src/data/taxonomy_nodes.cpp b/src/data/taxonomy_nodes.cpp index 4c4a3e0bf..de826b327 100644 --- a/src/data/taxonomy_nodes.cpp +++ b/src/data/taxonomy_nodes.cpp @@ -22,20 +22,39 @@ along with this program. If not, see . #include "taxonomy.h" #include "../util/log_stream.h" #include "../util/string/string.h" +#include "../util/io/text_input_file.h" +#include "../util/string/tokenizer.h" using namespace std; -void TaxonomyNodes::build(Serializer &out) +TaxonomyNodes::TaxonomyNodes(const string& file_name, const bool init_cache) +{ + TextInputFile f(file_name); + TaxId taxid, parent; + string rank; + while (!f.eof() && (f.getline(), !f.line.empty())) { + Util::String::Tokenizer(f.line, "\t|\t") >> taxid >> parent >> rank; + parent_.resize(taxid + 1, 0); + parent_[taxid] = parent; + rank_.resize(taxid + 1, Rank::none); + rank_[taxid] = Rank(rank.c_str()); + } + f.close(); + if (init_cache) + this->init_cache(); +} + +void TaxonomyNodes::save(Serializer &out) { task_timer timer("Building taxonomy nodes"); out.unset(Serializer::VARINT); - out << taxonomy.parent_; - out.write_raw(taxonomy.rank_); + out << parent_; + out.write_raw(rank_); timer.finish(); - message_stream << taxonomy.parent_.size() << " taxonomy nodes processed." << endl; + message_stream << parent_.size() << " taxonomy nodes processed." << endl; size_t rank_count[Rank::count]; std::fill(rank_count, rank_count + Rank::count, 0); - for (const Rank r : taxonomy.rank_) { + for (const Rank r : rank_) { ++rank_count[r]; } @@ -54,6 +73,10 @@ TaxonomyNodes::TaxonomyNodes(Deserializer &in, uint32_t db_build) rank_.resize(parent_.size()); in.read(rank_.data(), rank_.size()); } + init_cache(); +} + +void TaxonomyNodes::init_cache() { cached_.insert(cached_.end(), parent_.size(), false); contained_.insert(contained_.end(), parent_.size(), false); } @@ -91,10 +114,10 @@ unsigned TaxonomyNodes::get_lca(unsigned t1, unsigned t2) const return p; } -bool TaxonomyNodes::contained(unsigned query, const set &filter) +bool TaxonomyNodes::contained(TaxId query, const set &filter) { static const int max = 64; - if (query >= parent_.size()) + if (query >= (TaxId)parent_.size()) throw runtime_error(string("No taxonomy node found for taxon id ") + to_string(query)); if (cached_[query]) return contained_[query]; @@ -114,12 +137,12 @@ bool TaxonomyNodes::contained(unsigned query, const set &filter) return contained; } -bool TaxonomyNodes::contained(const vector query, const set &filter) +bool TaxonomyNodes::contained(const vector& query, const set &filter) { static const int max = 64; if (filter.find(1) != filter.end()) return true; - for (vector::const_iterator i = query.begin(); i != query.end(); ++i) + for (vector::const_iterator i = query.begin(); i != query.end(); ++i) if (contained(*i, filter)) return true; return false; @@ -142,8 +165,8 @@ unsigned TaxonomyNodes::rank_taxid(unsigned taxid, Rank rank) const { return 0; } -std::set TaxonomyNodes::rank_taxid(const std::vector &taxid, Rank rank) const { - set r; +std::set TaxonomyNodes::rank_taxid(const std::vector &taxid, Rank rank) const { + set r; for (unsigned i : taxid) r.insert(rank_taxid(i, rank)); return r; diff --git a/src/data/taxonomy_nodes.h b/src/data/taxonomy_nodes.h index 09688f6e6..84d38c048 100644 --- a/src/data/taxonomy_nodes.h +++ b/src/data/taxonomy_nodes.h @@ -26,6 +26,7 @@ along with this program. If not, see . #include #include "../util/io/serializer.h" #include "../util/io/deserializer.h" +#include "../basic/value.h" struct Rank { Rank() : @@ -58,8 +59,9 @@ struct Rank { struct TaxonomyNodes { + TaxonomyNodes(const std::string& file_name, const bool init_cache = false); TaxonomyNodes(Deserializer &in, uint32_t db_build); - static void build(Serializer &out); + void save(Serializer &out); unsigned get_parent(unsigned taxid) const { if (taxid >= parent_.size()) @@ -67,10 +69,10 @@ struct TaxonomyNodes return parent_[taxid]; } unsigned rank_taxid(unsigned taxid, Rank rank) const; - std::set rank_taxid(const std::vector &taxid, Rank rank) const; + std::set rank_taxid(const std::vector &taxid, Rank rank) const; unsigned get_lca(unsigned t1, unsigned t2) const; - bool contained(unsigned query, const std::set &filter); - bool contained(const std::vector query, const std::set &filter); + bool contained(TaxId query, const std::set &filter); + bool contained(const std::vector& query, const std::set &filter); private: @@ -79,8 +81,9 @@ struct TaxonomyNodes cached_[taxon_id] = true; contained_[taxon_id] = contained; } + void init_cache(); - std::vector parent_; + std::vector parent_; std::vector rank_; std::vector cached_, contained_; diff --git a/src/dna/TEMP_minimap_seeding.cpp b/src/dna/TEMP_minimap_seeding.cpp new file mode 100644 index 000000000..c0f1296d8 --- /dev/null +++ b/src/dna/TEMP_minimap_seeding.cpp @@ -0,0 +1,274 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../data/block/block.h" +#include "../data/sequence_file.h" +#include "TEMP_minimap_structures.h" +#include "TEMP_minimap_seeding.h" + + +/** + * + * @param char n + * @return char complement: the complementary nucleotide + */ +char complement(const char& n) +{ + switch(n) + { + case 'A': + return 'T'; + case 'T': + return 'A'; + case 'G': + return 'C'; + case 'C': + return 'G'; + default: + break; + } + assert(true) ;return (' '); +} + +/** + * Returns the reverse complement of a DNA String + * @param std::string sequence + * @return std::string reverse_complement + */ +std::string reverse_complement(std::string sequence){ + std::reverse(sequence.begin(), sequence.end()); + + transform(begin(sequence),end(sequence),begin(sequence),complement); + + return sequence; +} + +/** + * + * @param std::string s: sequence + * @param int i : position + * @param int k : k-mer size + * @param int r : reverse( if 1 return reverse complement) + * @return std::string: k-mer of size k at position i + */ +std::string sk(const std::string& s, unsigned long i, int k , int r){ + + std::string subsequence = s.substr(i,k); + + if (r) + return reverse_complement(subsequence); + else + return subsequence; +} +/** + * returns the specific hashValue of a nucleotide + * @param char: n {A,C,G,T} + * @return char : {0,1,2,3} + */ +int hash_values(const char& n){ + switch(n) + { + case 'A': + return '0'; + case 'C': + return '1' ; + case 'G': + return '2'; + case 'T': + return '3'; + default: + break; + + } + assert(true) ;return (' '); + +} +/** + * + * @param std::string s: sequence + * @return int h: the hashValue of a sequence + */ +int h(const std::string& s){ + + std::string hashedString; + + // transform nucleotide chars to hash Value + transform(begin(s), end(s), std::back_inserter(hashedString), hash_values); + + //Compute HashValue + int hashValue = 0; + for (unsigned long i = 0; i < hashedString.length(); i++) + hashValue += (hashedString[i] - 48) *pow(4, hashedString.length() - (i + 1)); + + return hashValue; +} + + +/** + * returns all minimizer of a given sequence s + * @param std::string s: the DNA sequence from which minimizers are to be extracted + * @param int k : the k-mer size + * @param int w : the window-size + * @return std::vector : dynamic array of minimizer {hashValue, position, strand} + */ +std::unordered_set minimizer_sketch(const std::string& s, const int& w, const int& k ){ + + std::unordered_set M; + + for(unsigned long i = 1; i <= s.length()-w-k+1; i++){ + int m = std::numeric_limits::max(); + + for( int j = 0; j < w; j++) { + int u = h(sk(s, i + j, k, 0)); + int v = h(sk(s, i + j, k, 1)); + + if (u != v) + m = std::min({m,u,v},std::less<>()); + } + for ( int j = 0; j < w; j++){ + int u = h(sk(s, i+j,k,0)); + int v = h(sk(s, i+j,k,1)); + + if (u < v && u ==m) + M.insert(Minimizer(m,i+j,0)); + else if ( v < u && v ==m) + M.insert(Minimizer(m,i+j,1)); + } + } + + + return M ; +} +/** + * maps all the minimizers of a given Target-Sequence to their HashValues + * @param std::vector fastA : list of Targets + * @param int w : window-size + * @param int k : k-mer size + * @return a HashMap, mapping hashValues to hashIndices {targetSequence, position, Strand} + */ +std::map> index(const std::string& target, const int& w, const int& k ){ + + std::map> H; + + int i = 0; + + std::unordered_set M = minimizer_sketch(target, w,k); + for( Minimizer m: M){ + if (H.find(m.hashValue) != H.end()){ + if(H[m.hashValue].back().position != m.position ){ + H[m.hashValue].emplace_back(HashIndex(m.position, m.strand)); + } + } + else + H[m.hashValue].emplace_back(HashIndex(m.position, m.strand)); + } + + + return H; +} + + +/** + * + * @param map H : a map, mapping hashValues to hashIndeces of the desired target + * @param std::string q : the given query sequence + * @param int k : k-mer size + * @param int w : window size + * @param int epsilon : the maximum band-width + * @return vector map(const std::map>& H, const std::string& q, const int& k, const int& w, const int& epsilon){ + + std::vector _result; + std::vector _matches; + + std::unordered_set M = minimizer_sketch(q,w,k); + + + for(Minimizer m: M ){ + try{ + const std::vector& h = H.at(m.hashValue); + + for(HashIndex hI: h){ + if(hI.strand == m.strand) // minimizer on same strand + _matches.emplace_back(MappingMatch(0, m.position - hI.position, hI.position)); + + + else // minimizer on different strand + _matches.emplace_back(MappingMatch(1,m.position + hI.position, hI.position)); + } + } + catch (const std::out_of_range& oor) {} ; + } + + sort(_matches.begin(),_matches.end(),comparer()); + + unsigned long b = 0; + + for(unsigned long e = 0; e < _matches.size(); e++){ + + if(e == _matches.size()-1 || _matches[e+1].strand != _matches[e].strand || _matches[e+1].start - _matches[e].start >= epsilon){ + + if(_matches[e].strand == 0) + _result.emplace_back(MinimizerHit(_matches[b].start + _matches[b].end, + _matches[b].end, + (_matches[e].start + _matches[e].end + k) - (_matches[b].start + _matches[b].end))); + b = e+1; + } + } + + return _result; +}; + +char const* convert_to_nucl(signed char a){ + switch (a) { + case 0: + return "A"; + case 1: + return "C"; + case 2: + return "G"; + case 3: + return "T"; + default: + return ""; + } +} + +std::string sequence_to_string(const Sequence& seq ){ + std::string translated; + for(int l = 0; l mainMap(const Search::Config &cfg,BlockId query_id,const Sequence &target){ + std::string query = cfg.query->seqs()[query_id].to_string(); + + + auto H = index(target.to_string(),10,15); + + auto matches = map(H,query,15,10,1); +/** + * for(minimizerHit mR: matches){ + std::cout << "match: target= " << mR.targetSequence +1 << " range:" + << mR.iMin+1 << "-" << mR.iMax+1 << " to " << mR.jMin+1 << "-" + << mR.jMax+1 << " reverse: " << mR.strand << std::endl; + + + std::cout << mR.iMax - mR.iMin << " : " << mR.jMax - mR.jMin << std::endl; + } + */ + + + return matches; + +} + + diff --git a/src/dna/TEMP_minimap_seeding.h b/src/dna/TEMP_minimap_seeding.h new file mode 100644 index 000000000..ffb2bbca6 --- /dev/null +++ b/src/dna/TEMP_minimap_seeding.h @@ -0,0 +1,14 @@ +#include +#include + +#include "TEMP_minimap_structures.h" +#include "../run/config.h" +#include "../data/block/block.h" +#include "../data/sequence_file.h" + + + + +std::vector mainMap(const Search::Config &cfg,BlockId query_id,const Sequence &target); + + diff --git a/src/dna/TEMP_minimap_structures.h b/src/dna/TEMP_minimap_structures.h new file mode 100644 index 000000000..38c43b4dc --- /dev/null +++ b/src/dna/TEMP_minimap_structures.h @@ -0,0 +1,108 @@ +#include +#include "../data/block/block.h" +#include "../data/sequence_file.h" + +#ifndef STRUCTURES +#define STRUCTURES + + +struct FastAentry{ + std::string header; + std::string sequence; + FastAentry(const std::string& header_ , const std::string& sequence_ ){ + header = header_; + sequence = sequence_; + }; +}; + + +struct Minimizer{ + int hashValue; + unsigned long position; + int strand; + + Minimizer(const int& hashvalue_, const unsigned long& position_, const int& strand_ ){ + hashValue = hashvalue_; + position = position_; + strand = strand_ ; + } + bool operator==(const Minimizer& t) const + { + return (this->hashValue == t.hashValue); + } + +}; + +class MyHashFunction { +public: + size_t operator()(const Minimizer& t) const + { + return t.hashValue; + } +}; + +struct HashIndex{ + int position; + int strand; + + HashIndex(const int position_ , const int strand_ ){ + position = position_; + strand = strand_; + } +}; + +struct MinimizerHit{ + + + MinimizerHit(const int i, const int j, const int length): + i_(i), + j_(j), + length_(length) + + {}; + + int iMin()const{return i_;} + int iMax()const{return i_ + length_;} + int jMin()const{return j_;} + int jMax()const{return j_ + length_;} + int length()const{return length_;} + int score()const{return score_;} + void score(int score){score_ = score;} + bool operator>(const MinimizerHit& hit)const + {return this->score() > hit.score();} + +private: + int i_,j_,length_, score_; + +}; + + +struct MappingMatch{ + int strand; + int start; + int end; + + MappingMatch(const int& strand_, const int& start_, const int& end_){ + strand = strand_; + start = start_; + end = end_; + } +}; + +// structure for sorting the mappingMatches +struct comparer{ + inline bool operator()(const MappingMatch& one, const MappingMatch& two){ + + + return ((one.strand < two.strand) || + (one.strand == two.strand && one.start < two.start) || + (one.strand == two.strand && one.start == two.start && one.end < two.end)); + + } +}; + + + + + +#endif diff --git a/src/dna/dna_index.cpp b/src/dna/dna_index.cpp new file mode 100644 index 000000000..1285635b6 --- /dev/null +++ b/src/dna/dna_index.cpp @@ -0,0 +1,21 @@ +#include "dna_index.h" +#include "../data/sequence_set.h" +#include "../data/queries.h" + + + +namespace Dna{ +Index::Index(Search::Config& cfg,char *ref_buffer) +{ + SequenceSet& ref_seqs = cfg.target->seqs(); + const SeedHistogram& ref_hst = cfg.target->hst(); + task_timer timer("Building reference seed array", true); + + + const EnumCfg enum_ref{&ref_hst.partition(), 0, 1, cfg.seed_encoding, nullptr, false, false, cfg.seed_complexity_cut, + MaskingAlgo::NONE,cfg.minimizer_window }; + + ref_idx_ = std::make_unique(*cfg.target, ref_hst.get(0), SeedPartitionRange(), ref_buffer, &no_filter, enum_ref); + +} +} diff --git a/src/dna/dna_index.h b/src/dna/dna_index.h new file mode 100644 index 000000000..ba81412cf --- /dev/null +++ b/src/dna/dna_index.h @@ -0,0 +1,13 @@ +#include "../run/config.h" +#include "../data/seed_array.h" + + +namespace Dna{ +class Index{ +public: + Index(Search::Config& cfg,char *ref_buffer); + +private: + std::unique_ptr ref_idx_; +}; +} \ No newline at end of file diff --git a/src/dna/smith_watermann.cpp b/src/dna/smith_watermann.cpp new file mode 100644 index 000000000..b4a8c7884 --- /dev/null +++ b/src/dna/smith_watermann.cpp @@ -0,0 +1,85 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Dimitrios Koutsogiannis + +Code developed by Dimitrios Koutsogiannis + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#include "smith_watermann.h" +#include "../data/block/block.h" +#include "../data/sequence_file.h" +#include + + +using std::vector; + + +namespace SmithWaterman{ + + int scoringFunction(int match, int mismatch, Letter first,Letter second){ + if(first == second) + return match; + else + return mismatch; + } + + int dynamic_programm(const Sequence &target, const Sequence &query, int match, int mismatch, int gapopen, int gapextend){ + vector> dp_matrix(query.length()+1,vector(target.length()+1,0)); + vector hgap(target.length()+1,0); + + int max = 0; + for(int i = 1; i < (int)dp_matrix.size(); i++){ + int vgap = 0; + for (int j = 1; j<(int)dp_matrix[0].size(); j++){ + int s = dp_matrix[i-1][j-1]+ scoringFunction(match,mismatch, query[i-1], target[j-1]); + s = std::max({s,hgap[j],vgap,0},std::less()); + dp_matrix[i][j] = s ; + vgap -= gapextend; + hgap[j] -= gapextend; + int open = s - gapopen - gapextend; + vgap = std::max(vgap, open); + hgap[j] = std::max(hgap[j], open); + + if(dp_matrix[i][j] > max){ + max = dp_matrix[i][j]; + + } + + } + } + + return max; + } + + std::pair, Extension::Stats> local_alignment(const Search::Config &cfg,BlockId query_id){ + vector matches; + SequenceSet& target_seqs = cfg.target->seqs(); + Sequence query_sequence = cfg.query->seqs()[query_id]; + + for(int i = 0; i < target_seqs.size(); i ++){ + Sequence target_sequence = target_seqs[i]; + Extension::Match m = Extension::Match(query_id,target_sequence,::Stats::TargetMatrix(),0,0); + int score = dynamic_programm(target_sequence, query_sequence,cfg.score_builder->reward(), cfg.score_builder->penalty(), cfg.score_builder->gap_open(), cfg.score_builder->gap_extend()) ; + m.hsp.emplace_back(Hsp(false,score)); + m.hsp.back().bit_score = cfg.score_builder->blast_bit_Score(score); + m.hsp.back().evalue = cfg.score_builder->blast_eValue(score,query_sequence.length()); + matches.emplace_back(m); + + } + return { matches, Extension::Stats()}; + } + +} \ No newline at end of file diff --git a/src/util/system_c.h b/src/dna/smith_watermann.h similarity index 63% rename from src/util/system_c.h rename to src/dna/smith_watermann.h index 016077534..651cef2f1 100644 --- a/src/util/system_c.h +++ b/src/dna/smith_watermann.h @@ -1,6 +1,8 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2017 Benjamin Buchfink +Copyright (C) 2022 Dimitrios Koutsogiannis + +Code developed by Dimitrios Koutsogiannis This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,13 +18,13 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef SYSTEM_C_H_ -#define SYSTEM_C_H_ +#pragma once +#include "../basic/match.h" +#include "../run/config.h" +#include "../align/extend.h" + +namespace SmithWaterman { -#ifdef _MSC_VER -typedef __int64 ssize_t; -#define STDIN_FILENO _fileno(stdin) -#define STDOUT_FILENO _fileno(stdout) -#endif +std::pair, Extension::Stats> local_alignment(const Search::Config &cfg, BlockId query_id); -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/dna/wfa2_test.cpp b/src/dna/wfa2_test.cpp new file mode 100644 index 000000000..7e8092821 --- /dev/null +++ b/src/dna/wfa2_test.cpp @@ -0,0 +1,145 @@ +#include +#include "../lib/wfa2/bindings/cpp/WFAligner.hpp" +#include "wfa2_test.h" +#include "TEMP_minimap_seeding.h" +#include "TEMP_minimap_structures.h" +#include "../basic/config.h" + + +namespace WaveExtension { + +Hsp cigar_to_hsp(const Stats::Blastn_Score &score_builder, const std::string& cigar,const Sequence &target, const Sequence &query){ + Hsp out = Hsp(true,0); + + int pattern_pos = 0, text_pos = 0, deletion_count = 0, insertion_count = 0, score = 0; + bool insertion=false, deletion = false; + out.query_range.begin_ = 0 ; + out.subject_range.begin_ = 0 ; + for (auto a: cigar) { + switch (a) { + case 'M': + case 'X': + if(deletion){ + out.push_gap(op_deletion,deletion_count,target.data()+deletion_count+text_pos); + score -= (score_builder.gap_open() + (score_builder.gap_extend()*(deletion_count-1))); + deletion = false; + deletion_count = 0; + } + if(insertion){ + out.transcript.push_back(op_insertion, (unsigned)insertion_count); + score -= (score_builder.gap_open() + (score_builder.gap_extend()*(insertion_count-1))); + insertion = false; + insertion_count = 0; + } + out.push_match(target[text_pos], query[pattern_pos],true); + if (target[text_pos] == query[pattern_pos]) + score += score_builder.reward(); + else + score += score_builder.penalty(); + pattern_pos++; text_pos++; + break; + case 'D': + insertion = true; + insertion_count += 1; + pattern_pos ++; + break; + case 'I': + deletion = true; + deletion_count += 1; + text_pos ++; + break; + default: + break; + } + } + + out.score = score; + out.query_range.end_ = pattern_pos ; + out.subject_range.end_ = text_pos ; + out.transcript.push_terminator(); + out.target_seq = target; + out.query_source_range = out.query_range; + out.bit_score = score_builder.blast_bit_Score(out.score); + out.evalue = score_builder.blast_eValue(out.score,query.length()); + return out; +} + +struct ExtendedSeed{ + ExtendedSeed(int i_min, int i_max, int j_min, int j_max): + i_min_(i_min), + i_max_(i_max), + j_min_(j_min), + j_max_(j_max) + {} + + int i_min_extended()const{return i_min_;}; + int i_max_extended()const{return i_max_;}; + int j_min_extended()const{return j_min_;}; + int j_max_extended()const{return j_max_;}; + +private: + int i_min_, i_max_, j_min_, j_max_; +}; + + + +void calculate_ungapped_scores(MinimizerHit &hit,const Sequence &target, const Sequence &query){ + int score = 0; + int i = 0; + + while(query[hit.iMin() - i] == target[hit.jMin() - i]){ + score++; + i++; + } + + i = 1 ; + while(query[hit.iMin() + i] == target[hit.jMin() + i]){ + score++; + i++; + } + hit.score(score); +} + +bool intersection(const MinimizerHit &hit,const std::vector &extended){ + return std::any_of(extended.begin(), extended.end(), [hit](ExtendedSeed s) + {return hit.iMin() >= s.i_min_extended() && hit.iMax() <= s.i_max_extended() && hit.jMin() >= s.j_min_extended() && hit.jMax() <= s.j_max_extended() ;}); +} + +std::pair, Extension::Stats> extend(const Search::Config &cfg,BlockId query_id){ + SequenceSet& target_seqs = cfg.target->seqs(); + std::vector matches; + wfa::WFAlignerGapAffine aligner(-cfg.score_builder->penalty(),cfg.score_builder->gap_open(),cfg.score_builder->gap_extend(),wfa::WFAligner::Alignment,wfa::WFAligner::MemoryHigh); + + + for(int i = 0; i < target_seqs.size(); i ++){ + Sequence target_sequence = target_seqs[i]; + std::vector hits = mainMap(cfg,query_id,target_sequence); + Extension::Match m = Extension::Match(i,target_sequence,::Stats::TargetMatrix(),0,0); + + std::for_each(hits.begin(),hits.end(), [&]( MinimizerHit &hit){ + calculate_ungapped_scores(hit,target_sequence,cfg.query->seqs()[query_id]); + }); + + sort(hits.begin(),hits.end(), std::greater<>()); + std::string query = cfg.query->seqs()[query_id].to_string(); + std::vector extended_hit_positions; + + for(auto hit: hits){ + if ((intersection(hit,extended_hit_positions))) + continue; + std::string target = target_sequence.to_string(); + aligner.alignEndsFree(query,hit.iMin(),hit.iMax(),target,hit.jMin(),hit.jMax()); // Align + std::string cigar = aligner.getAlignmentCigar(); + Hsp out = cigar_to_hsp(*cfg.score_builder,cigar,target_sequence,cfg.query->seqs()[query_id]); + if(out.evalue < config.max_evalue){ + m.hsp.push_back(out); + extended_hit_positions.emplace_back(hit.iMin(),hit.iMax(),hit.jMin(),hit.jMax()); + } + + } + if(!m.hsp.empty()) + matches.push_back(m); + } + return { matches, Extension::Stats()}; +} +} \ No newline at end of file diff --git a/src/dna/wfa2_test.h b/src/dna/wfa2_test.h new file mode 100644 index 000000000..9a1134823 --- /dev/null +++ b/src/dna/wfa2_test.h @@ -0,0 +1,11 @@ +#include "../run/config.h" +#include "TEMP_minimap_structures.h" +#include "../basic/match.h" +#include "../align/extend.h" + + +namespace WaveExtension{ +std::pair, Extension::Stats> extend(const Search::Config &cfg,BlockId query_id); +} + + diff --git a/src/dp/dp.h b/src/dp/dp.h index a53442210..b59085469 100644 --- a/src/dp/dp.h +++ b/src/dp/dp.h @@ -30,6 +30,7 @@ along with this program. If not, see . #include "../data/sequence_set.h" #include "../stats/cbs.h" #include "flags.h" +#include "../util/parallel/thread_pool.h" struct DpTarget { @@ -42,7 +43,7 @@ struct DpTarget {} int i1, j1, ident, len; }; - enum { BLANK = -1 }; + enum { BLANK = -1, MIN_LETTERS = 3 }; static Loc banded_cols(const Loc qlen, const Loc tlen, const Loc d_begin, const Loc d_end) { const Loc pos = std::max(d_end - 1, 0) - (d_end - 1); const Loc d0 = d_begin; @@ -56,23 +57,27 @@ struct DpTarget target_idx(BLANK), matrix(nullptr) {} - DpTarget(const Sequence &seq, int true_target_len, int d_begin, int d_end, int target_idx, int qlen, const Stats::TargetMatrix* matrix = nullptr, const CarryOver& carry_over = CarryOver()) : + DpTarget(const Sequence &seq, int true_target_len, int d_begin, int d_end, Interval chaining_target_range, Score chaining_score, BlockId target_idx, int qlen, const Stats::TargetMatrix* matrix = nullptr, const CarryOver& carry_over = CarryOver(), const Anchor& anchor = Anchor()) : seq(seq), d_begin(d_begin), d_end(d_end), cols(banded_cols(qlen, seq.length(), d_begin, d_end)), true_target_len(true_target_len), + chaining_target_range(chaining_target_range), + chaining_score(chaining_score), target_idx(target_idx), carry_over(carry_over), - matrix(matrix) + matrix(matrix), + anchor(anchor) { } - DpTarget(const Sequence& seq, int true_target_len, int target_idx, const Stats::TargetMatrix* matrix = nullptr, const CarryOver& carry_over = CarryOver()): + DpTarget(const Sequence& seq, int true_target_len, BlockId target_idx, const Stats::TargetMatrix* matrix = nullptr, const CarryOver& carry_over = CarryOver()): seq(seq), d_begin(), d_end(), cols(), true_target_len(true_target_len), + chaining_score(0), target_idx(target_idx), carry_over(carry_over), matrix(matrix) @@ -110,11 +115,30 @@ struct DpTarget int matrix_scale() const { return adjusted_matrix() ? config.cbs_matrix_scale : 1; } + int64_t cells(DP::Flags flags, Loc qlen) const { + return flag_any(flags, DP::Flags::FULL_MATRIX) ? (int64_t)seq.length() * (int64_t)qlen + : int64_t(d_end - d_begin) * (int64_t)cols; + } + bool extend_right(Loc qlen) const { + //return anchor.query_end() < qlen && anchor.subject_end() < seq.length() + return std::min(qlen - anchor.query_end(), seq.length() - anchor.subject_end()) >= MIN_LETTERS; + } + bool extend_left() const { + //return anchor.query_begin() > 0 && anchor.subject_begin() > 0 + return std::min(anchor.query_begin(), anchor.subject_begin()) >= MIN_LETTERS; + } + friend std::ostream& operator<<(std::ostream& s, const DpTarget& t) { + s << t.seq << '\t' << t.d_begin << '\t' << t.d_end << '\t' << t.cols << '\t' << t.true_target_len << '\t' << t.target_idx; + return s; + } Sequence seq; Loc d_begin, d_end, cols, true_target_len; - int target_idx; + Interval chaining_target_range; + Score chaining_score; + BlockId target_idx; CarryOver carry_over; const Stats::TargetMatrix* matrix; + Anchor anchor; }; struct DpStat @@ -142,12 +166,14 @@ namespace DP { struct Params { const Sequence query; + const char* query_id; const Frame frame; const int query_source_len; const int8_t* const composition_bias; const Flags flags; - const HspValues v; + HspValues v; Statistics& stat; + ThreadPool* thread_pool; }; enum { BINS = 6, SCORE_BINS = 3, ALGO_BINS = 2 }; @@ -160,6 +186,18 @@ using Targets = std::array, BINS>; struct NoCBS { constexpr void* operator[](int i) const { return nullptr; } }; + +namespace AnchoredSwipe { + +struct Config { + Sequence query; + const int8_t* query_cbs; + Score score_hint; + Statistics& stats; + ThreadPool* thread_pool; +}; + +} namespace Swipe { @@ -172,9 +210,10 @@ namespace BandedSwipe { DECL_DISPATCH(std::list, swipe, (const Targets& targets, Params& params)) DECL_DISPATCH(std::list, swipe_set, (const SequenceSet::ConstIterator begin, const SequenceSet::ConstIterator end, Params& params)) DECL_DISPATCH(unsigned, bin, (HspValues v, int query_len, int score, int ungapped_score, const int64_t dp_size, unsigned score_width, const Loc mismatch_est)) +DECL_DISPATCH(std::list, anchored_swipe, (Targets& targets, const DP::AnchoredSwipe::Config& cfg)) } } -DECL_DISPATCH(std::list, banded_3frame_swipe, (const TranslatedSequence &query, Strand strand, vector::iterator target_begin, vector::iterator target_end, DpStat &stat, bool score_only, bool parallel)) +DECL_DISPATCH(std::list, banded_3frame_swipe, (const TranslatedSequence &query, Strand strand, std::vector::iterator target_begin, std::vector::iterator target_end, DpStat &stat, bool score_only, bool parallel)) diff --git a/src/dp/flags.h b/src/dp/flags.h index a4a6e5f4c..bdca87ca8 100644 --- a/src/dp/flags.h +++ b/src/dp/flags.h @@ -5,7 +5,7 @@ namespace DP { enum class Flags { NONE = 0, PARALLEL = 1, FULL_MATRIX = 2, SEMI_GLOBAL = 4 }; - DEF_ENUM_FLAG_OPERATORS(Flags) + DEFINE_ENUM_FLAG_OPERATORS(Flags) } @@ -26,7 +26,7 @@ enum class HspValues : unsigned { COORDS = QUERY_COORDS | TARGET_COORDS }; -DEF_ENUM_FLAG_OPERATORS(HspValues) +DEFINE_ENUM_FLAG_OPERATORS(HspValues) static inline bool have_coords(const HspValues v) { return flag_any(v, HspValues::TRANSCRIPT) || flag_all(v, HspValues::QUERY_COORDS | HspValues::TARGET_COORDS); diff --git a/src/dp/hsp_traits.h b/src/dp/hsp_traits.h deleted file mode 100644 index 3726f31b0..000000000 --- a/src/dp/hsp_traits.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef HSP_TRAITS_H_ -#define HSP_TRAITS_H_ - -#include -#include -#include "../util/interval.h" -#include "../basic/diagonal_segment.h" -#include "../basic/match.h" - -struct Hsp_traits -{ - Hsp_traits(unsigned frame) : - d_min(std::numeric_limits::max()), - d_max(std::numeric_limits::min()), - score(0), - frame((int)frame) - {} - Hsp_traits(const interval &query_source_range) : - query_source_range(query_source_range) - {} - Hsp_traits(const Hsp &hsp) - {} - Hsp_traits(int d_min, int d_max, int score, int frame, const interval& query_range, const interval& subject_range): - d_min(d_min), - d_max(d_max), - score(score), - frame(frame), - query_range(query_range), - subject_range(subject_range) - {} - int partial_score(const Diagonal_segment &d) const - { - const double overlap = std::max(d.subject_range().overlap_factor(subject_range), d.query_range().overlap_factor(query_range)); - return int((1 - overlap)*d.score); - } - int partial_score(const Hsp_traits &x) const - { - const double overlap = std::max(x.subject_range.overlap_factor(subject_range), x.query_range.overlap_factor(query_range)); - return int((1 - overlap)*x.score); - } - bool disjoint(const Diagonal_segment &d) const - { - return intersect(query_range, d.query_range()).length() == 0 && intersect(subject_range, d.subject_range()).length() == 0; - } - bool disjoint(const Hsp_traits &x) const - { - return intersect(query_range, x.query_range).length() == 0 && intersect(subject_range, x.subject_range).length() == 0; - } - bool rel_disjoint(const Diagonal_segment &d) const - { - return intersect(query_range, d.query_range()).length() == 0 || intersect(subject_range, d.subject_range()).length() == 0; - } - bool rel_disjoint(const Hsp_traits &x) const - { - return intersect(query_range, x.query_range).length() == 0 || intersect(subject_range, x.subject_range).length() == 0; - } - bool collinear(const Hsp_traits &x) const - { - const int di = x.query_range.begin_ - query_range.begin_, dj = x.subject_range.begin_ - subject_range.begin_; - return (di >= 0 && dj >= 0) || (di <= 0 && dj <= 0); - } - bool collinear(const Diagonal_segment &d) const - { - const int di = d.i - query_range.begin_, dj = d.j - subject_range.begin_; - return (di >= 0 && dj >= 0) || (di <= 0 && dj <= 0); - } - static bool cmp_diag(const Hsp_traits& x, const Hsp_traits& y) - { - return x.frame < y.frame || (x.frame == y.frame && x.d_min < y.d_min); - } - struct Frame - { - unsigned operator()(const Hsp_traits &x) const - { - return x.frame; - } - }; - int d_min, d_max, score, frame; - interval query_source_range, query_range, subject_range; -}; - -#endif \ No newline at end of file diff --git a/src/dp/needleman_wunsch.h b/src/dp/needleman_wunsch.h new file mode 100644 index 000000000..36bd30070 --- /dev/null +++ b/src/dp/needleman_wunsch.h @@ -0,0 +1,30 @@ +#pragma once +#include "../basic/sequence.h" +#include "../stats/score_matrix.h" +#include "../util/util.h" + +inline int nw_semiglobal(Sequence query, Sequence target) { + Matrix m(query.length() + 1, target.length() + 1); + std::vector hgap(query.length() + 1); + for (Loc i = 1; i <= query.length(); ++i) { + hgap[i] = -score_matrix.gap_open() - i * score_matrix.gap_extend(); + m[i][0] = hgap[i] - score_matrix.gap_open() - score_matrix.gap_extend(); + } + int score = INT_MIN; + for (int j = 1; j <= target.length(); ++j) { + int vgap = -score_matrix.gap_open() - score_matrix.gap_extend(); + for (int i = 1; i <= query.length(); ++i) { + int s = m[i - 1][j - 1] + score_matrix(query[i - 1], target[j - 1]); + s = std::max(s, vgap); + s = std::max(s, hgap[i]); + m[i][j] = s; + vgap -= score_matrix.gap_extend(); + hgap[i] -= score_matrix.gap_extend(); + int open = s - score_matrix.gap_open() - score_matrix.gap_extend(); + vgap = std::max(vgap, open); + hgap[i] = std::max(hgap[i], open); + } + score = std::max(score, m[query.length()][j]); + } + return score; +} \ No newline at end of file diff --git a/src/dp/pfscan/pfscan.cpp b/src/dp/pfscan/pfscan.cpp new file mode 100644 index 000000000..315eed169 --- /dev/null +++ b/src/dp/pfscan/pfscan.cpp @@ -0,0 +1,180 @@ +#include "pfscan.h" +#include "smith_waterman.h" +#include "../../util/sequence/sequence.h" +#include "../ungapped.h" + +using std::min; + +namespace DP { namespace PrefixScan { namespace DISPATCH_ARCH { + +Hsp align16(const Config& cfg) { + using SC = StaticConfig<::DISPATCH_ARCH::ScoreVector, Local>; + Hsp hsp = banded_smith_waterman(cfg); + //hsp.evalue = score_matrix.evalue(hsp.score, cfg.query.length(), cfg.target.length()); + return hsp; +} + +Hsp align8(const Config& cfg) { +#ifdef __SSE4_1__ + using SC = StaticConfig<::DISPATCH_ARCH::ScoreVector, Local>; + Hsp hsp = banded_smith_waterman(cfg); + //hsp.evalue = score_matrix.evalue(hsp.score, cfg.query.length(), cfg.target.length()); + return hsp; +#else + return Hsp(); +#endif +} + +static Hsp align_dispatch_score_16(const Config& cfg) { + using SC = StaticConfig<::DISPATCH_ARCH::ScoreVector, Anchored>; + Config cfg16(cfg); + cfg16.adjust_band(16); + cfg16.score_bias = score_matrix.gap_extend() * (cfg16.d_end - cfg16.d_begin) * 2; + const Hsp h = banded_smith_waterman(cfg16); + cfg.stats.inc(Statistics::EXT16); + if (h.score < SCHAR_MAX) + cfg.stats.inc(Statistics::EXT_WASTED_16); + if (h.score == (Score)SHRT_MAX - cfg16.score_bias) { + task_timer timer; + using SC = StaticConfig; + const Hsp h = banded_smith_waterman(cfg); + cfg.stats.inc(Statistics::TIME_EXT_32, timer.microseconds()); + cfg.stats.inc(Statistics::EXT32); + return h; + } + return h; +} + +static Hsp align_dispatch_score(const Config& cfg) { + if (cfg.query.length() >= SHRT_MAX || cfg.target.length() >= SHRT_MAX) { + task_timer timer; + using SC = StaticConfig; + const Hsp h = banded_smith_waterman(cfg); + cfg.stats.inc(Statistics::TIME_EXT_32, timer.microseconds()); + cfg.stats.inc(Statistics::EXT32); + return h; + } + else { +#ifdef __SSE4_1__ + if (config.no_8bit_extension || cfg.band() / 32 > SCHAR_MAX || cfg.band() <= 16 || cfg.score_hint >= 95 || cfg.band() > 128 + || score_matrix.gap_open() + score_matrix.gap_extend() * max(-cfg.d_begin, cfg.d_end) > SCHAR_MAX) { +#endif + return align_dispatch_score_16(cfg); +#ifdef __SSE4_1__ + } + else { + using SC = StaticConfig<::DISPATCH_ARCH::ScoreVector, Anchored>; + Config cfg8(cfg); + cfg8.adjust_band(32); + const Hsp h = banded_smith_waterman(cfg8); + cfg.stats.inc(Statistics::EXT8); + if (h.score == (Score)SCHAR_MAX) { + cfg.stats.inc(Statistics::EXT_OVERFLOW_8); + return align_dispatch_score_16(cfg); + } + return h; + } +#endif + } +} + +static double length_fraction(Loc pos, Loc len, Loc anchor_len) { + return double(len - pos) / (len - anchor_len); +} + +static Hsp align_right(Loc i, Loc j, Loc d_begin, Loc d_end, Score prefix_score, const Config& cfg) { + const Sequence query(cfg.query.subseq(i, cfg.query.length())), + target(cfg.target.subseq(j, cfg.target.length())); + vector profile; + vector profile8; + profile.reserve(AMINO_ACID_COUNT); + profile8.reserve(AMINO_ACID_COUNT); + for (size_t a = 0; a < AMINO_ACID_COUNT; ++a) { + profile.push_back(cfg.query_profile[a] + i); + profile8.push_back(cfg.query_profile8[a] + i); + } + const int band = std::max(32, Loc((d_end - d_begin) * 0.15)); + d_begin -= band; + d_end += band - 1; + const Loc d0 = Geo::clip_diag(Geo::diag_sub_matrix(d_begin, i, j), query.length(), target.length()), + d1 = Geo::clip_diag(Geo::diag_sub_matrix(d_end, i, j), query.length(), target.length()); + Config cfg_r{ + query, + target, + cfg.query_seqid, + cfg.target_seqid, + d0, + d1, + cfg.hint_target_range.length() > 0 ? Interval(0, cfg.hint_target_range.end_ - j) : Interval(), + profile.data(), + nullptr, + profile8.data(), + nullptr, + cfg.stats, + 0, + std::max(score_matrix.gap_extend() * (d1 - d0), score_matrix.rawscore(config.gapped_xdrop)), + //score_matrix.rawscore(config.gapped_xdrop) + cfg.score_hint - prefix_score + }; + Hsp h = align_dispatch_score(cfg_r); + h.query_range.end_ += i; + h.subject_range.end_ += j; + return h; +} + +static Hsp align_left(Loc i, Loc j, Loc d_begin, Loc d_end, Score suffix_score, const Config& cfg) { + const vector ql(cfg.query.reverse()), + tl(cfg.target.reverse()); + const Loc qlen = cfg.query.length(), tlen = cfg.target.length(); + Config cfg_l{ + Sequence(ql), + Sequence(tl), + cfg.query_seqid, + cfg.target_seqid, + cfg.d_begin, + cfg.d_end, + cfg.hint_target_range.length() > 0 ? Interval(0, tlen - cfg.hint_target_range.begin_) : Interval(), + cfg.query_profile_rev, + nullptr, + cfg.query_profile_rev8, + nullptr, + cfg.stats, + cfg.score_bias, + cfg.xdrop, + cfg.score_hint + }; + Hsp h = align_right(qlen - 1 - i, tlen - 1 - j, Geo::rev_diag(d_end - 1, qlen, tlen), Geo::rev_diag(d_begin, qlen, tlen) + 1, suffix_score, cfg_l); + h.query_range.begin_ = qlen - 1 - (h.query_range.end_ - 1); + h.subject_range.begin_ = tlen - 1 - (h.subject_range.end_ - 1); + return h; +} + +Hsp align_anchored(const Anchor& anchor, const Config& cfg) { + assert(anchor.diag() >= cfg.d_begin && anchor.diag() < cfg.d_end); + Hsp h(false, anchor.score); + h.query_range = anchor.query_range(); + h.subject_range = anchor.subject_range(); + if (anchor.query_end() < cfg.query.length() && anchor.subject_end() < cfg.target.length()) { + Hsp r = align_right(anchor.query_end(), anchor.subject_end(), anchor.d_min_right, anchor.d_max_right + 1, anchor.prefix_score, cfg); + h.score += r.score; + h.query_range.end_ = r.query_range.end_; + h.subject_range.end_ = r.subject_range.end_; + } + if (anchor.query_begin() > 0 && anchor.subject_begin() > 0) { + const Score suffix_score = cfg.score_hint - anchor.prefix_score + anchor.score; + Hsp l = align_left(anchor.query_begin() - 1, anchor.subject_begin() - 1, anchor.d_min_left, anchor.d_max_left + 1, suffix_score, cfg); + h.score += l.score; + h.query_range.begin_ = l.query_range.begin_; + h.subject_range.begin_ = l.subject_range.begin_; + } + h.query_source_range = h.query_range; + h.bit_score = score_matrix.bitscore(h.score); + h.evalue = score_matrix.evalue(h.score, cfg.query.length(), cfg.target.length()); + if (h.evalue > config.max_evalue) { + h.evalue = DBL_MAX; + h.score = 0; + } + return h; +} + +}}} \ No newline at end of file diff --git a/src/dp/pfscan/pfscan.h b/src/dp/pfscan/pfscan.h new file mode 100644 index 000000000..009cba7a1 --- /dev/null +++ b/src/dp/pfscan/pfscan.h @@ -0,0 +1,33 @@ +#pragma once + +#include "../basic/statistics.h" +#include "../../basic/match.h" +#include "../score_profile.h" + +namespace DP { namespace PrefixScan { + +struct Config { + Sequence query, target; + const char* query_seqid, *target_seqid; + Loc d_begin, d_end; + Interval hint_target_range; + const int16_t* const* query_profile, * const* query_profile_rev; + const int8_t* const* query_profile8, * const* query_profile_rev8; + Statistics& stats; + Score score_bias, xdrop, score_hint; + + void adjust_band(Loc channels) { + const Loc b = std::max((d_end - d_begin + channels - 1) / channels * channels, channels); + d_begin -= (b - d_end + d_begin) / 2; + d_end = d_begin + b; + } + Loc band() const { + return d_end - d_begin; + } +}; + +DECL_DISPATCH(Hsp, align16, (const Config& cfg)) +DECL_DISPATCH(Hsp, align8, (const Config& cfg)) +DECL_DISPATCH(Hsp, align_anchored, (const Anchor& anchor, const Config& cfg)) + +}} \ No newline at end of file diff --git a/src/dp/pfscan/simd.h b/src/dp/pfscan/simd.h new file mode 100644 index 000000000..ff79e8ba7 --- /dev/null +++ b/src/dp/pfscan/simd.h @@ -0,0 +1,157 @@ +// Prefix scan implemenation by Daniel Liu, see: https://github.com/Daniel-Liu-c0deb0t/block-aligner + +/* +MIT License + +Copyright (c) 2021 Daniel Liu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#pragma once +#include "../score_vector_int16.h" + +namespace DP { namespace PrefixScan { namespace DISPATCH_ARCH { + +#if ARCH_ID == 2 || ARCH_ID == 3 + +template +static inline std::pair<::DISPATCH_ARCH::ScoreVector, ::DISPATCH_ARCH::ScoreVector> prefix_scan_consts(const ::DISPATCH_ARCH::ScoreVector gap) { + __m256i shift1 = _mm256_slli_si256(gap.data_, 2); + shift1 = _mm256_adds_epi16(shift1, gap.data_); + __m256i shift2 = _mm256_slli_si256(shift1, 4); + shift2 = _mm256_adds_epi16(shift2, shift1); + __m256i shift4 = _mm256_slli_si256(shift2, 8); + shift4 = _mm256_adds_epi16(shift4, shift2); + + __m256i correct1 = _mm256_srli_si256(_mm256_shufflehi_epi16(shift4, 0xff), 8); + correct1 = _mm256_permute4x64_epi64(correct1, 5); + correct1 = _mm256_adds_epi16(correct1, shift4); + + return { ::DISPATCH_ARCH::ScoreVector(correct1), ::DISPATCH_ARCH::ScoreVector(shift4) }; +} + +template +static inline ::DISPATCH_ARCH::ScoreVector prefix_scan( + ::DISPATCH_ARCH::ScoreVector input, + const ::DISPATCH_ARCH::ScoreVector gap_extend, + const ::DISPATCH_ARCH::ScoreVector gap_cost_lane) +{ + //const __m256i shrt_min = _mm256_set1_epi16(SHRT_MIN); + __m256i shift1 = _mm256_slli_si256(input.data_, 2); + //shift1 = _mm256_blend_epi16(shift1, shrt_min, 1); + shift1 = _mm256_adds_epi16(shift1, gap_extend.data_); + shift1 = _mm256_max_epi16(input.data_, shift1); + __m256i shift2 = _mm256_slli_si256(shift1, 4); + //shift2 = _mm256_blend_epi16(shift2, shrt_min, 3); + shift2 = _mm256_adds_epi16(shift2, _mm256_slli_epi16(gap_extend.data_, 1)); + shift2 = _mm256_max_epi16(shift1, shift2); + __m256i shift4 = _mm256_slli_si256(shift2, 8); + //shift4 = _mm256_blend_epi16(shift4, shrt_min, 15); + shift4 = _mm256_adds_epi16(shift4, _mm256_slli_epi16(gap_extend.data_, 2)); + shift4 = _mm256_max_epi16(shift2, shift4); + __m256i correct1 = _mm256_shufflehi_epi16(shift4, 0xff); + correct1 = _mm256_permute4x64_epi64(correct1, 0x50); + correct1 = _mm256_adds_epi16(correct1, gap_cost_lane.data_); + return ::DISPATCH_ARCH::ScoreVector(_mm256_max_epi16(shift4, correct1)); +} + +template +static inline std::pair<::DISPATCH_ARCH::ScoreVector, ::DISPATCH_ARCH::ScoreVector> prefix_scan_consts(const ::DISPATCH_ARCH::ScoreVector gap) { + __m256i shift1 = _mm256_slli_si256(gap.data_, 1); + shift1 = _mm256_adds_epi8(shift1, gap.data_); + __m256i shift2 = _mm256_slli_si256(shift1, 2); + shift2 = _mm256_adds_epi8(shift2, shift1); + __m256i shift4 = _mm256_slli_si256(shift2, 4); + shift4 = _mm256_adds_epi8(shift4, shift2); + __m256i shift8 = _mm256_slli_si256(shift4, 8); + shift8 = _mm256_adds_epi8(shift8, shift4); + + __m256i correct1 = _mm256_srli_si256(_mm256_set1_epi8(_mm256_extract_epi8(shift8, 15)), 8); + correct1 = _mm256_permute4x64_epi64(correct1, 5); + correct1 = _mm256_adds_epi8(correct1, shift8); + + return { ::DISPATCH_ARCH::ScoreVector(correct1), ::DISPATCH_ARCH::ScoreVector(shift8) }; +} + +template +static inline ::DISPATCH_ARCH::ScoreVector prefix_scan( + ::DISPATCH_ARCH::ScoreVector input, + const ::DISPATCH_ARCH::ScoreVector gap_extend, + const ::DISPATCH_ARCH::ScoreVector gap_cost_lane) +{ + const __m256i schar_min = _mm256_set1_epi8(SCHAR_MIN); + __m256i shift1 = _mm256_slli_si256(input.data_, 1); + shift1 = _mm256_or_si256(shift1, _mm256_set_epi8('\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', SCHAR_MIN, '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', SCHAR_MIN)); + shift1 = _mm256_adds_epi8(shift1, gap_extend.data_); + shift1 = _mm256_max_epi8(input.data_, shift1); + __m256i shift2 = _mm256_slli_si256(shift1, 2); + //shift2 = _mm256_blendv_epi8(shift2, schar_min, _mm256_set_epi8('\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\xff', '\xff', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\xff', '\xff')); + shift2 = _mm256_blend_epi16(shift2, schar_min, 1); + shift2 = _mm256_adds_epi8(shift2, _mm256_set1_epi8(-2)); + shift2 = _mm256_max_epi8(shift1, shift2); + __m256i shift4 = _mm256_slli_si256(shift2, 4); + //shift4 = _mm256_blendv_epi8(shift4, schar_min, _mm256_set_epi8('\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\xff', '\xff', '\xff', '\xff', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\xff', '\xff', '\xff', '\xff')); + shift4 = _mm256_blend_epi16(shift4, schar_min, 3); + shift4 = _mm256_adds_epi8(shift4, _mm256_set1_epi8(-4)); + shift4 = _mm256_max_epi8(shift2, shift4); + __m256i shift8 = _mm256_slli_si256(shift4, 8); + //shift8 = _mm256_blendv_epi8(shift8, schar_min, _mm256_set_epi8('\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff')); + shift8 = _mm256_blend_epi16(shift8, schar_min, 15); + shift8 = _mm256_adds_epi8(shift8, _mm256_set1_epi8(-8)); + shift8 = _mm256_max_epi8(shift4, shift8); + __m256i correct1 = _mm256_set1_epi8(_mm256_extract_epi8(shift8, 15)); + correct1 = _mm256_blendv_epi8(schar_min, correct1, _mm256_set_epi8('\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0')); + correct1 = _mm256_adds_epi8(correct1, gap_cost_lane.data_); + return ::DISPATCH_ARCH::ScoreVector(_mm256_max_epi8(shift8, correct1)); +} + +#else + +template +static inline std::pair<::DISPATCH_ARCH::ScoreVector, ::DISPATCH_ARCH::ScoreVector> prefix_scan_consts(const ::DISPATCH_ARCH::ScoreVector gap) { + return { ::DISPATCH_ARCH::ScoreVector(), ::DISPATCH_ARCH::ScoreVector() }; +} + +template +static inline ::DISPATCH_ARCH::ScoreVector prefix_scan(::DISPATCH_ARCH::ScoreVector input, const ::DISPATCH_ARCH::ScoreVector gap_extend, const ::DISPATCH_ARCH::ScoreVector gap_cost_lane) { + return ::DISPATCH_ARCH::ScoreVector(); +} + +template +static inline std::pair<::DISPATCH_ARCH::ScoreVector, ::DISPATCH_ARCH::ScoreVector> prefix_scan_consts(const ::DISPATCH_ARCH::ScoreVector gap) { + return { ::DISPATCH_ARCH::ScoreVector(), ::DISPATCH_ARCH::ScoreVector() }; +} + +template +static inline ::DISPATCH_ARCH::ScoreVector prefix_scan(::DISPATCH_ARCH::ScoreVector input, const ::DISPATCH_ARCH::ScoreVector gap_extend, const ::DISPATCH_ARCH::ScoreVector gap_cost_lane) { + return ::DISPATCH_ARCH::ScoreVector(); +} + +#endif + +static inline std::pair prefix_scan_consts(int32_t gap) { + return { -1,-1 }; +} + +static inline int32_t prefix_scan(int32_t input, int32_t, int32_t) { + return input; +} + +}}} \ No newline at end of file diff --git a/src/dp/pfscan/smith_waterman.h b/src/dp/pfscan/smith_waterman.h new file mode 100644 index 000000000..84c5447da --- /dev/null +++ b/src/dp/pfscan/smith_waterman.h @@ -0,0 +1,200 @@ +#include "pfscan.h" +#include "../../basic/config.h" +#include "../../util/geo/geo.h" +#include "../dp/score_vector_int8.h" +#include "../dp/score_vector_int16.h" +#include "../../util/memory/alignment.h" +#include "simd.h" + +using std::max; +using std::min; +using std::vector; +using std::fill; +using std::numeric_limits; +using std::tie; + +namespace DP { namespace PrefixScan { namespace DISPATCH_ARCH { + +static const double XDROP_MAX_MASKED_RATIO = 0.5; + +struct Local {}; +struct Anchored {}; + +template +struct StaticConfig { + using Sv = ScoreVector; + using Logic = L; +}; + +template +static inline void saturate(Sv& sv, const Sv& zero, Anchored) { +} + +template +static inline void saturate(Sv& sv, const Sv& zero, Local) { + sv = max(sv, zero); +} + +template +static inline void init_scores(Score* score, Loc d, Loc band, Score bias, Local) { + fill(score, score + band, (Score)bias); +} + +template +static inline void init_scores(Score* score, Loc d, Loc band, Score bias, Anchored) { + const Score e = score_matrix.gap_extend(); + Score s = bias - score_matrix.gap_open(); + for (Loc i = d - 1; i >= 0; --i) { + s -= e; + score[i] = s; + } + score[d] = bias; + s = bias - score_matrix.gap_open(); + for (Loc i = d + 1; i < band; ++i) { + s -= e; + score[i] = s; + } +} + +template +static inline const int16_t* const* get_profile(const Config& cfg, Score) { + return cfg.query_profile; +} + +static inline const int8_t* const* get_profile(const Config& cfg, int8_t) { + return cfg.query_profile8; +} + +template +static inline Sv load_profile(const Score* ptr) { + return Sv(ptr); +} + +template<> +inline int32_t load_profile(const int16_t* ptr) { + return *ptr; +} + +template +static inline Loc clip_i0(Loc i0) { + return max(i0 + (-i0 / channels) * channels, i0); +} + +Statistics::value cell_stat(int8_t) { + return Statistics::DP_CELLS_8; +} + +Statistics::value cell_stat(int16_t) { + return Statistics::DP_CELLS_16; +} + +Statistics::value cell_stat(int32_t) { + return Statistics::DP_CELLS_32; +} + +template +Hsp FLATTEN banded_smith_waterman(const Config& cfg) { + using Sv = typename StaticConfig::Sv; + using Score = typename ::DISPATCH_ARCH::ScoreTraits::Score; + using Logic = typename StaticConfig::Logic; + + constexpr Loc SCORE_MIN = (Loc)numeric_limits::min(), SCORE_MAX = (Loc)numeric_limits::max(); + const Loc channels = ::DISPATCH_ARCH::ScoreTraits::CHANNELS; + + task_timer timer; + + const Loc band = cfg.d_end - cfg.d_begin, + qlen = cfg.query.length(), + tlen = cfg.target.length(), + j0 = max(Geo::j(0, cfg.d_end - 1), 0), + j1 = min(Geo::j(qlen, cfg.d_begin), tlen); + Loc i0 = Geo::i(j0, cfg.d_begin), + i1 = Geo::i(j0, cfg.d_end); + + assert(band % channels == 0); + auto query_profile = get_profile(cfg, Score()); + vector> scores(band); + vector> hgap(band + 1, numeric_limits::min() + score_matrix.gap_extend()); + init_scores(scores.data(), -i0, band, (Score)cfg.score_bias, Logic()); + const Sv gap_extend = Sv(-score_matrix.gap_extend()), + gap_open = Sv(-score_matrix.gap_open()), + zero(cfg.score_bias); + Sv pf_const2, pf_const1, max_score((Score)cfg.score_bias), max_j(-1), max_i(-1); + tie(pf_const1, pf_const2) = prefix_scan_consts(gap_extend); + + Loc j = j0; + size_t n = 0; + for (; j < j1; ++j) { + const Loc i0_ = clip_i0(i0); + const auto sv_offset = (i0_ - i0); + Score* score_it = scores.data() + sv_offset; + Score* hgap_it = hgap.data() + sv_offset; + auto profile = query_profile[cfg.target[j]] + i0_; + Sv vgap(numeric_limits::min() + score_matrix.gap_extend()); + Sv col_max(SHRT_MIN), col_max_i(-1), counter(0); + for (Loc i = i0_; i < i1; i += channels) { + Sv score = load_sv_aligned(score_it) + load_profile(profile); + Sv hgap = load_sv(hgap_it + 1); + hgap += gap_extend; + score = max(score, hgap); + + Sv v = score + gap_open; + v = prefix_scan(v, gap_extend, pf_const2); + v = max(v, vgap + pf_const1); + score = max(score, v); + saturate(score, zero, Logic()); + + store_aligned(score, score_it); + + vgap = Sv(extract(v)); + + hgap = max(hgap, score + gap_open); + store_aligned(hgap, hgap_it); + + const Sv gt_mask = score > col_max; + col_max_i = blend(col_max_i, counter, gt_mask); + col_max = max(col_max, score); + + score_it += channels; + hgap_it += channels; + profile += channels; + counter += Sv(1); + ++n; + } + ++i0; + i1 = min(i1 + 1, qlen); + const Sv gt_mask = col_max > max_score; + max_j = blend(max_j, Sv(min(j + SCORE_MIN, SCORE_MAX)), gt_mask); + max_i = blend(max_i, col_max_i, gt_mask); + max_score = max(max_score, col_max); + + if ((j & 31) == 31) { + const auto best = max_entry(max_score); + if (best.first == numeric_limits::max()) + break; + if (j < cfg.hint_target_range.end_) + continue; + const auto col_best = max_entry(col_max); + if ((int32_t)best.first - (int32_t)col_best.first >= cfg.xdrop + && cfg.target.subseq_clipped(j - cfg.xdrop, j + 1).masked_letter_ratio() < XDROP_MAX_MASKED_RATIO + && cfg.query.subseq_clipped(i1 - cfg.xdrop, i1 + 1).masked_letter_ratio() < XDROP_MAX_MASKED_RATIO) + break; + } + } + Hsp out; + const auto best = max_entry(max_score); + out.score = ::DISPATCH_ARCH::ScoreTraits::int_score(best.first) - cfg.score_bias; + if (out.score > 0) { + const Loc max_j_ = (Loc)extract(max_j, best.second); + if (max_j_ == SCORE_MAX) + out.score = SCORE_MAX; + out.subject_range.end_ = max_j_ - SCORE_MIN + 1; + out.query_range.end_ = clip_i0(i0 - j + out.subject_range.end_ - 1) + channels * extract(max_i, best.second) + best.second + 1; + assert(out.query_range.end_ > 0 && out.subject_range.end_ > 0); + } + cfg.stats.inc(cell_stat(Score()), n * channels); + cfg.stats.inc(Statistics::TIME_SW, timer.microseconds()); + return out; +} + +}}} \ No newline at end of file diff --git a/src/util/double_buffer.h b/src/dp/scalar/double_buffer.h similarity index 66% rename from src/util/double_buffer.h rename to src/dp/scalar/double_buffer.h index 2756255bf..040bc89b4 100644 --- a/src/util/double_buffer.h +++ b/src/dp/scalar/double_buffer.h @@ -16,39 +16,35 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef DOUBLE_BUFFER_H_ -#define DOUBLE_BUFFER_H_ +#pragma once -#include - -using std::vector; -using std::pair; - -template -struct Double_buffer +template +struct DoubleBuffer { - inline void init(size_t size, size_t padding, size_t padding_front, _t init) + inline void init(size_t size, size_t padding, size_t padding_front, T init) { const size_t total = size + padding + padding_front; data_.clear(); - data_.resize(total*2); + data_.resize(total * 2); ptr1 = &data_[padding_front]; - ptr2 = &data_[total+padding_front]; - for(size_t i=0;i get(int) - { std::swap(ptr1, ptr2); return pair<_t*,_t*> (ptr2, ptr1); } + inline std::pair get(int) + { + std::swap(ptr1, ptr2); return std::pair(ptr2, ptr1); + } - inline _t* last() - { return ptr1; } + inline T* last() + { + return ptr1; + } private: - _t *ptr1, *ptr2; - vector<_t> data_; + T* ptr1, * ptr2; + std::vector data_; }; - -#endif /* DOUBLE_BUFFER_H_ */ diff --git a/src/dp/scalar/scalar.h b/src/dp/scalar/scalar.h new file mode 100644 index 000000000..29687520c --- /dev/null +++ b/src/dp/scalar/scalar.h @@ -0,0 +1,2 @@ +#pragma once +void smith_waterman(Sequence q, Sequence s, Hsp& out); \ No newline at end of file diff --git a/src/dp/scalar/smith_waterman.cpp b/src/dp/scalar/smith_waterman.cpp new file mode 100644 index 000000000..782ed41a9 --- /dev/null +++ b/src/dp/scalar/smith_waterman.cpp @@ -0,0 +1,285 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2013-2017 Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#include +#include "../dp.h" +#include "double_buffer.h" +#include "../../util/util.h" +#include "traceback.h" +#include "../output/output_format.h" + +using std::vector; +using std::pair; + +struct Local {}; +struct Global {}; + +template +_score saturate(_score x) +{ + return x; +} + +template<> +int saturate(int x) +{ + return std::max(x, 0); +} + +template +void set_max_score(_score s, _score& max_score) +{ +} + +template<> +void set_max_score(int s, int& max_score) +{ + max_score = std::max(max_score, s); +} + +template +struct Fixed_score_buffer +{ + + inline void init(size_t col_size, size_t cols, _t init) + { + col_size_ = col_size; + data_.clear(); + data_.reserve(col_size * cols); + data_.resize(col_size); + for (size_t i = 0; i < col_size; ++i) + data_[i] = init; + } + + std::pair find(_t s) const + { + const int i = int(std::find(data_.begin(), data_.end(), s) - data_.begin()); + return std::pair(int(i % col_size_), int(i / col_size_)); + } + + inline std::pair<_t*, _t*> get() + { + data_.resize(data_.size() + col_size_); + _t* ptr = last(); + return std::pair<_t*, _t*>(ptr - col_size_, ptr); + } + + inline _t* last() + { + return &*(data_.end() - col_size_); + } + + const _t* column(int col) const + { + return &data_[col_size_ * col]; + } + + _t operator()(int i, int j) const + { + return data_[j * col_size_ + i]; + } + + friend std::ostream& operator<<(std::ostream& s, const Fixed_score_buffer& buf) + { + s << '\t'; + for (int j = 0; j < int(buf.data_.size() / buf.col_size_); ++j) + s << j << '\t'; + s << std::endl; + for (int i = 0; i < int(buf.col_size_); ++i) { + s << i << '\t'; + for (int j = 0; j < int(buf.data_.size() / buf.col_size_); ++j) + s << buf(i, j) << '\t'; + s << std::endl; + } + return s; + } + +private: + std::vector<_t> data_; + size_t col_size_; + +}; + +template +struct Dp_matrix +{ + + struct Column_iterator + { + + inline Column_iterator(const pair<_score*, _score*>& score, _score* hgap, int query_len, int col) : + score_(score), + hgap_(hgap), + end_(score_.second + query_len + 1), + i_(0) + { + *score_.first = saturate<_score, _mode>(col == 0 ? 0 : -score_matrix.gap_open() - col * score_matrix.gap_extend()); + ++score_.second; + } + + inline int row() const + { + return i_; + } + + inline bool valid() const + { + return score_.second < end_; + } + + inline _score& score() + { + return *score_.second; + } + + inline _score diag() const + { + return *score_.first; + } + + inline _score& hgap() + { + return *hgap_; + } + + inline void operator++() + { + ++i_; + ++score_.first; + ++score_.second; + ++hgap_; + } + + private: + pair<_score*, _score*> score_; + _score* hgap_; + const _score* const end_; + int i_; + + }; + + inline Column_iterator column(int j) + { + return Column_iterator(score_.get(), hgap_.data(), query_len_, j); + } + + inline Dp_matrix(int query_len, int subject_len) : + query_len_(query_len) + { + score_.init(query_len + 1, subject_len + 1, 0); + hgap_.clear(); + hgap_.insert(hgap_.end(), query_len, std::numeric_limits::min() + score_matrix.gap_extend()); + int* score = score_.last(); + int g = -score_matrix.gap_open() - score_matrix.gap_extend(); + for (int i = 1; i <= query_len; ++i) + score[i] = saturate<_score, _mode>(g--); + } + + const Fixed_score_buffer<_score>& score_buffer() const + { + return score_; + } + +private: + + const int query_len_; + static thread_local Fixed_score_buffer<_score> score_; + static thread_local vector<_score> hgap_; + +}; + +template thread_local Fixed_score_buffer<_score> Dp_matrix<_score, _mode>::score_; +template thread_local vector<_score> Dp_matrix<_score, _mode>::hgap_; + +template +const Fixed_score_buffer<_score>& needleman_wunsch(Sequence query, Sequence subject, int& max_score, const _mode&, const _score&) +{ + using std::max; + const int gap_open = score_matrix.gap_open() + score_matrix.gap_extend(), gap_extend = score_matrix.gap_extend(); + int m = 0; + + Dp_matrix<_score, _mode> mtx((unsigned)query.length(), (unsigned)subject.length()); + + for (int j = 0; j < (int)subject.length(); ++j) { + typename Dp_matrix<_score, _mode>::Column_iterator it = mtx.column(j); + _score vgap = std::numeric_limits::min() + gap_extend; + for (; it.valid(); ++it) { + const _score match_score = score_matrix(subject[j], query[it.row()]); + const _score s = saturate<_score, _mode>(max(max(it.diag() + match_score, vgap), it.hgap())); + const _score open = s - gap_open; + vgap = max(vgap - gap_extend, open); + it.hgap() = max(it.hgap() - gap_extend, open); + it.score() = s; + set_max_score<_score, _mode>(s, m); + } + } + + max_score = m; + return mtx.score_buffer(); +} + +template const Fixed_score_buffer& needleman_wunsch(Sequence query, Sequence subject, int& max_score, const Local&, const int&); + +void smith_waterman(Sequence q, Sequence s, Hsp& out) +{ + int max_score; + const Fixed_score_buffer& dp = needleman_wunsch(q, s, max_score, Local(), int()); + pair max_pos = dp.find(max_score); + + const int gap_open = score_matrix.gap_open(), gap_extend = score_matrix.gap_extend(); + int l, i = max_pos.first, j = max_pos.second, score; + out.clear(); + out.score = dp(i, j); + out.query_range.end_ = i; + out.subject_range.end_ = j; + + while ((score = dp(i, j)) > 0) { + const int match_score = score_matrix(q[i - 1], s[j - 1]); + if (score == match_score + dp(i - 1, j - 1)) { + if (q[i - 1] == s[j - 1]) { + out.transcript.push_back(op_match); + ++out.identities; + } + else { + out.transcript.push_back(op_substitution, s[j - 1]); + } + --i; + --j; + ++out.length; + } + else if (have_hgap(dp, i, j, gap_open, gap_extend, l)) { + for (; l > 0; l--) { + out.transcript.push_back(op_deletion, s[--j]); + ++out.length; + } + } + else if (have_vgap(dp, i, j, gap_open, gap_extend, l)) { + out.transcript.push_back(op_insertion, (unsigned)l); + out.length += l; + i -= l; + } + else + throw std::runtime_error("Traceback error."); + } + + out.query_range.begin_ = i; + out.subject_range.begin_ = j; + out.query_source_range = out.query_range; + out.transcript.reverse(); + out.transcript.push_terminator(); +} \ No newline at end of file diff --git a/src/dp/scalar/traceback.h b/src/dp/scalar/traceback.h new file mode 100644 index 000000000..9b0972060 --- /dev/null +++ b/src/dp/scalar/traceback.h @@ -0,0 +1,86 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2013-2017 Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once +#include "../basic/match.h" +#include "../stats/score_matrix.h" + +template +bool have_vgap(const _matrix& dp, + int i, + int j, + int gap_open, + int gap_extend, + int& l) +{ + int score = dp(i, j); + l = 1; + --i; + while (i > 0) { + if (score == dp(i, j) - gap_open - l * gap_extend) + return true; + --i; + ++l; + } + return false; +} + +template +bool have_hgap(const _matrix& dp, + int i, + int j, + int gap_open, + int gap_extend, + int& l) +{ + int score = dp(i, j); + l = 1; + --j; + while (j > 0) { + if (score == dp(i, j) - gap_open - l * gap_extend) + return true; + --j; + ++l; + } + return false; +} + +template +int have_diag(const _matrix& dp, + int i, + int j, + const Sequence& query, + const Sequence& subject, + bool log) +{ + int l = 0; + while (i > 0 && j > 0) { + const int match_score = score_matrix(query[i - 1], subject[j - 1]); + + if (dp(i, j) == match_score + dp(i - 1, j - 1)) { + /*if (log) + printf("i=%i j=%i score=%i subject=%c query=%c\n", i, j, dp(i, j), value_traits.alphabet[(int)subject[j - 1]], value_traits.alphabet[(int)query[i - 1]]);*/ + ++l; + --i; + --j; + } + else + break; + } + return l; +} diff --git a/src/dp/scan_diags.cpp b/src/dp/scan_diags.cpp index 37fa6f12b..74c417c43 100644 --- a/src/dp/scan_diags.cpp +++ b/src/dp/scan_diags.cpp @@ -28,7 +28,7 @@ using namespace DISPATCH_ARCH; namespace DP { namespace DISPATCH_ARCH { -void scan_diags128(const LongScoreProfile& qp, Sequence s, int d_begin, int j_begin, int j_end, int *out) +void scan_diags128(const LongScoreProfile& qp, Sequence s, int d_begin, int j_begin, int j_end, int *out) { #ifdef __AVX2__ using Sv = ScoreVector; @@ -126,7 +126,7 @@ void scan_diags128(const LongScoreProfile& qp, Sequence s, int d_begin, int j_be #endif } -void scan_diags64(const LongScoreProfile& qp, Sequence s, int d_begin, int j_begin, int j_end, int* out) +void scan_diags64(const LongScoreProfile& qp, Sequence s, int d_begin, int j_begin, int j_end, int* out) { #ifdef __AVX2__ using Sv = ScoreVector; @@ -200,7 +200,7 @@ void scan_diags64(const LongScoreProfile& qp, Sequence s, int d_begin, int j_beg #endif } -void scan_diags(const LongScoreProfile& qp, Sequence s, int d_begin, int d_end, int j_begin, int j_end, int* out) +void scan_diags(const LongScoreProfile& qp, Sequence s, int d_begin, int d_end, int j_begin, int j_end, int* out) { #ifdef __AVX2__ using Sv = ScoreVector; diff --git a/src/dp/scan_diags.h b/src/dp/scan_diags.h index 987d75aba..b2cca2088 100644 --- a/src/dp/scan_diags.h +++ b/src/dp/scan_diags.h @@ -1,15 +1,11 @@ -#ifndef SCAN_DIAGS_H -#define SCAN_DIAGS_H - +#pragma once #include "score_profile.h" namespace DP { -DECL_DISPATCH(void, scan_diags128, (const LongScoreProfile& qp, Sequence s, int d_begin, int j_begin, int j_end, int* out)) -DECL_DISPATCH(void, scan_diags64, (const LongScoreProfile& qp, Sequence s, int d_begin, int j_begin, int j_end, int* out)) -DECL_DISPATCH(void, scan_diags, (const LongScoreProfile& qp, Sequence s, int d_begin, int d_end, int j_begin, int j_end, int* out)) +DECL_DISPATCH(void, scan_diags128, (const LongScoreProfile& qp, Sequence s, int d_begin, int j_begin, int j_end, int* out)) +DECL_DISPATCH(void, scan_diags64, (const LongScoreProfile& qp, Sequence s, int d_begin, int j_begin, int j_end, int* out)) +DECL_DISPATCH(void, scan_diags, (const LongScoreProfile& qp, Sequence s, int d_begin, int d_end, int j_begin, int j_end, int* out)) DECL_DISPATCH(int, diag_alignment, (const int* s, int count)) -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/dp/score_profile.cpp b/src/dp/score_profile.cpp new file mode 100644 index 000000000..6f67168e2 --- /dev/null +++ b/src/dp/score_profile.cpp @@ -0,0 +1,54 @@ +#include "score_profile.h" +#include "score_vector.h" +#include "score_vector_int8.h" +#include "score_vector_int16.h" +#include "../util/util.h" + +using std::array; + +namespace DP { namespace DISPATCH_ARCH { + +template +LongScoreProfile make_profile(Sequence seq, const int8_t* cbs, int64_t padding) +{ + LongScoreProfile p; + p.padding = std::max(padding, (int64_t)LongScoreProfile::DEFAULT_PADDING); + for (unsigned l = 0; l < AMINO_ACID_COUNT; ++l) { + const int8_t* scores = &score_matrix.matrix8()[l << 5]; + p.data[l].reserve(round_up(seq.length(), 32) + 2 * p.padding); + p.data[l].insert(p.data[l].end(), p.padding, -1); +#if ARCH_ID == 2 + using Sv = ::DISPATCH_ARCH::ScoreVector; + constexpr auto CHANNELS = ::DISPATCH_ARCH::ScoreTraits::CHANNELS; + alignas(32) array buf; + for (Loc i = 0; i < seq.length(); i += CHANNELS) { + __m256i s = _mm256_loadu_si256((const __m256i*)(seq.data() + i)); + Sv scores(l, s); + if (cbs && l < TRUE_AA) + scores += Sv(cbs + i); + store_expanded(scores, buf.data()); + p.data[l].insert(p.data[l].end(), buf.begin(), buf.end()); + } + p.data[l].erase(p.data[l].end() - round_up(seq.length(), (Loc)CHANNELS) + seq.length(), p.data[l].end()); +#else + for (Loc i = 0; i < seq.length(); ++i) { + int8_t score = scores[(int)seq[i]]; + if (cbs) + score += cbs[i]; + p.data[l].push_back(score); + } +#endif + p.data[l].insert(p.data[l].end(), p.padding, -1); + } + return p; +} + +LongScoreProfile make_profile8(Sequence seq, const int8_t* cbs, int64_t padding) { + return make_profile(seq, cbs, padding); +} + +LongScoreProfile make_profile16(Sequence seq, const int8_t* cbs, int64_t padding) { + return make_profile(seq, cbs, padding); +} + +}} \ No newline at end of file diff --git a/src/dp/score_profile.h b/src/dp/score_profile.h index 2f49c3ade..c98bbd3bb 100644 --- a/src/dp/score_profile.h +++ b/src/dp/score_profile.h @@ -22,48 +22,44 @@ along with this program. If not, see . #pragma once #include #include "../basic/sequence.h" -#include "score_vector.h" #include "../basic/value.h" #include "../stats/hauser_correction.h" +template struct LongScoreProfile { - LongScoreProfile() + enum { DEFAULT_PADDING = 128 }; + LongScoreProfile(int64_t padding = DEFAULT_PADDING): + padding(std::max(padding, (int64_t)DEFAULT_PADDING)) {} - LongScoreProfile(Sequence seq, const Bias_correction &cbs) - { - for (unsigned l = 0; l < AMINO_ACID_COUNT; ++l) { - const int8_t* scores = &score_matrix.matrix8()[l << 5]; - data[l].reserve(seq.length() + 2 * padding); - data[l].insert(data[l].end(), padding, 0); - for (Loc i = 0; i < seq.length(); ++i) - data[l].push_back(scores[(int)seq[i]] + cbs.int8[i]); - data[l].insert(data[l].end(), padding, 0); - } - } - LongScoreProfile(Sequence seq) - { - set(seq); - } - void set(Sequence seq) { - for (unsigned l = 0; l < AMINO_ACID_COUNT; ++l) { - const int8_t* scores = &score_matrix.matrix8()[l << 5]; - data[l].clear(); - data[l].reserve(seq.length() + 2 * padding); - data[l].insert(data[l].end(), padding, 0); - for (Loc i = 0; i < seq.length(); ++i) - data[l].push_back(scores[(int)seq[i]]); - data[l].insert(data[l].end(), padding, 0); - } - } size_t length() const { return data[0].size() - 2 * padding; } - const int8_t* get(Letter l, int i) const + const Score* get(Letter l, int i) const { return &data[(int)l][i + padding]; } - std::vector data[AMINO_ACID_COUNT]; - enum { padding = 128 }; + std::vector pointers(int offset) const { + std::vector v; + v.reserve(AMINO_ACID_COUNT); + for (size_t i = 0; i < AMINO_ACID_COUNT; ++i) + v.push_back(get(Letter(i), offset)); + return v; + } + LongScoreProfile reverse() const { + LongScoreProfile r(*this); + for (size_t i = 0; i < AMINO_ACID_COUNT; ++i) + std::reverse(r.data[i].begin(), r.data[i].end()); + return r; + } + std::vector data[AMINO_ACID_COUNT]; + int64_t padding; }; + +namespace DP { + +DECL_DISPATCH(LongScoreProfile, make_profile8, (Sequence seq, const int8_t* cbs, int64_t padding)) +DECL_DISPATCH(LongScoreProfile, make_profile16, (Sequence seq, const int8_t* cbs, int64_t padding)) + +} \ No newline at end of file diff --git a/src/dp/score_vector.h b/src/dp/score_vector.h index 2a138d460..a3c9fe8b0 100644 --- a/src/dp/score_vector.h +++ b/src/dp/score_vector.h @@ -87,15 +87,49 @@ static inline void store_sv(int32_t sv, int32_t *dst) } template -static Sv load_sv(const typename DISPATCH_ARCH::ScoreTraits::Score* ptr) { +static inline void store_aligned(Sv sv, typename DISPATCH_ARCH::ScoreTraits::Score* ptr) { + sv.store_aligned(ptr); +} + +template<> +inline void store_aligned(int32_t sv, int32_t* ptr) { + *ptr = sv; +} + +template +static inline Sv load_sv(const typename DISPATCH_ARCH::ScoreTraits::Score* ptr) { return Sv(ptr); } template<> -int32_t load_sv(const int32_t* x) { +inline int32_t load_sv(const int32_t* x) { return *x; } +template +static inline Sv load_sv_aligned(const typename DISPATCH_ARCH::ScoreTraits::Score* ptr) { + return Sv::load_aligned(ptr); +} + +template<> +inline int32_t load_sv_aligned(const int32_t* ptr) { + return *ptr; +} + +template +static inline int32_t extract(int32_t x) { + return x; +} + +static inline int32_t extract(int32_t x, int i) { + return x; +} + +template +static inline typename DISPATCH_ARCH::ScoreTraits::Score extract(Sv sv, int i) { + return sv[i]; +} + template static Sv blend_sv(const typename DISPATCH_ARCH::ScoreTraits::Score a, const typename DISPATCH_ARCH::ScoreTraits::Score b, const uint32_t mask) { const uint32_t CHANNELS = DISPATCH_ARCH::ScoreTraits::CHANNELS; @@ -113,12 +147,31 @@ int32_t blend_sv(const int32_t a, const int32_t b, const uint32_t mask) return mask ? b : a; } +static inline int32_t blend(const int32_t a, const int32_t b, const uint32_t mask) { + return mask ? b : a; +} + +static inline std::pair max_entry(int32_t x) { + return { x,0 }; +} + +template +static inline std::pair::Score, int> max_entry(Sv sv) { + std::array::Score, DISPATCH_ARCH::ScoreTraits::CHANNELS> s; + sv.store(s.data()); + const auto i = std::max_element(s.begin(), s.end()); + return { *i, int(i - s.begin()) }; +} + #ifdef __SSE2__ template static inline void store_sv(const DISPATCH_ARCH::ScoreVector<_t, DELTA> &sv, _p *dst) { -#if ARCH_ID == 2 +#if ARCH_ID == 3 + //_mm512_storeu_si512((__m512i*)dst, sv.data_); + sv.store(dst); +#elif ARCH_ID == 2 _mm256_storeu_si256((__m256i*)dst, sv.data_); #else _mm_storeu_si128((__m128i*)dst, sv.data_); diff --git a/src/dp/score_vector_int16.h b/src/dp/score_vector_int16.h index 604793328..4755bbce5 100644 --- a/src/dp/score_vector_int16.h +++ b/src/dp/score_vector_int16.h @@ -25,7 +25,7 @@ along with this program. If not, see . namespace DISPATCH_ARCH { -#if ARCH_ID == 2 +#if ARCH_ID == 2 || ARCH_ID == 3 template struct ScoreVector @@ -34,17 +34,17 @@ struct ScoreVector typedef __m256i Register; ScoreVector() : - data_(::SIMD::_mm256_set1_epi16(DELTA)) + data_(_mm256_set1_epi16(DELTA)) {} explicit ScoreVector(int x) { - data_ = ::SIMD::_mm256_set1_epi16(x); + data_ = _mm256_set1_epi16(x); } explicit ScoreVector(int16_t x) { - data_ = ::SIMD::_mm256_set1_epi16(x); + data_ = _mm256_set1_epi16(x); } explicit ScoreVector(__m256i data) : @@ -64,17 +64,17 @@ struct ScoreVector const __m256i* row_lo = reinterpret_cast(&score_matrix.matrix8u_low()[a << 5]); const __m256i* row_hi = reinterpret_cast(&score_matrix.matrix8u_high()[a << 5]); - __m256i high_mask = _mm256_slli_epi16(_mm256_and_si256(seq, ::SIMD::_mm256_set1_epi8('\x10')), 3); + __m256i high_mask = _mm256_slli_epi16(_mm256_and_si256(seq, _mm256_set1_epi8('\x10')), 3); __m256i seq_low = _mm256_or_si256(seq, high_mask); - __m256i seq_high = _mm256_or_si256(seq, _mm256_xor_si256(high_mask, ::SIMD::_mm256_set1_epi8('\x80'))); + __m256i seq_high = _mm256_or_si256(seq, _mm256_xor_si256(high_mask, _mm256_set1_epi8('\x80'))); __m256i r1 = _mm256_load_si256(row_lo); __m256i r2 = _mm256_load_si256(row_hi); __m256i s1 = _mm256_shuffle_epi8(r1, seq_low); __m256i s2 = _mm256_shuffle_epi8(r2, seq_high); - data_ = _mm256_and_si256(_mm256_or_si256(s1, s2), ::SIMD::_mm256_set1_epi16(255)); - data_ = _mm256_subs_epi16(data_, ::SIMD::_mm256_set1_epi16(score_matrix.bias())); + data_ = _mm256_and_si256(_mm256_or_si256(s1, s2), _mm256_set1_epi16(255)); + data_ = _mm256_subs_epi16(data_, _mm256_set1_epi16(score_matrix.bias())); } ScoreVector operator+(const ScoreVector& rhs) const @@ -104,7 +104,7 @@ struct ScoreVector } ScoreVector& operator++() { - data_ = _mm256_adds_epi16(data_, ::SIMD::_mm256_set1_epi16(1)); + data_ = _mm256_adds_epi16(data_, _mm256_set1_epi16(1)); return *this; } @@ -114,14 +114,19 @@ struct ScoreVector return *this; } - friend ScoreVector blend(const ScoreVector&v, const ScoreVector&w, const ScoreVector&mask) { - return ScoreVector(_mm256_blendv_epi8(v.data_, w.data_, mask.data_)); + template + ScoreVector shift_left() const { + return ScoreVector(_mm256_slli_si256(data_, i)); } ScoreVector operator==(const ScoreVector&v) const { return ScoreVector(_mm256_cmpeq_epi16(data_, v.data_)); } + ScoreVector operator>(const ScoreVector& v) const { + return ScoreVector(_mm256_cmpgt_epi16(data_, v.data_)); + } + friend uint32_t cmp_mask(const ScoreVector&v, const ScoreVector&w) { return _mm256_movemask_epi8(_mm256_cmpeq_epi16(v.data_, w.data_)); } @@ -136,6 +141,11 @@ struct ScoreVector _mm256_storeu_si256((__m256i*)ptr, data_); } + void store_aligned(int16_t* ptr) const + { + _mm256_store_si256((__m256i*)ptr, data_); + } + int16_t operator[](int i) const { int16_t d[16]; store(d); @@ -168,10 +178,24 @@ struct ScoreVector return s; } + static ScoreVector load_aligned(const int16_t* x) { + return ScoreVector(_mm256_load_si256((const __m256i*)x)); + } + __m256i data_; }; +template +static inline int16_t extract(ScoreVector sv) { + return (int16_t)_mm256_extract_epi16(sv.data_, i); +} + +template +static inline ScoreVector blend(const ScoreVector& v, const ScoreVector& w, const ScoreVector& mask) { + return ScoreVector(_mm256_blendv_epi8(v.data_, w.data_, mask.data_)); +} + #elif defined(__SSE2__) template @@ -181,17 +205,17 @@ struct ScoreVector typedef __m128i Register; inline ScoreVector() : - data_(::SIMD::_mm_set1_epi16(DELTA)) + data_(_mm_set1_epi16(DELTA)) {} explicit ScoreVector(int x) { - data_ = ::SIMD::_mm_set1_epi16(x); + data_ = _mm_set1_epi16(x); } explicit ScoreVector(int16_t x) { - data_ = ::SIMD::_mm_set1_epi16(x); + data_ = _mm_set1_epi16(x); } explicit ScoreVector(__m128i data) : @@ -211,16 +235,16 @@ struct ScoreVector { const __m128i *row = reinterpret_cast(&score_matrix.matrix8u()[a << 5]); - __m128i high_mask = _mm_slli_epi16(_mm_and_si128(seq, ::SIMD::_mm_set1_epi8('\x10')), 3); + __m128i high_mask = _mm_slli_epi16(_mm_and_si128(seq, _mm_set1_epi8('\x10')), 3); __m128i seq_low = _mm_or_si128(seq, high_mask); - __m128i seq_high = _mm_or_si128(seq, _mm_xor_si128(high_mask, ::SIMD::_mm_set1_epi8('\x80'))); + __m128i seq_high = _mm_or_si128(seq, _mm_xor_si128(high_mask, _mm_set1_epi8('\x80'))); __m128i r1 = _mm_load_si128(row); __m128i r2 = _mm_load_si128(row + 1); __m128i s1 = _mm_shuffle_epi8(r1, seq_low); __m128i s2 = _mm_shuffle_epi8(r2, seq_high); - data_ = _mm_and_si128(_mm_or_si128(s1, s2), ::SIMD::_mm_set1_epi16(255)); - data_ = _mm_subs_epi16(data_, ::SIMD::_mm_set1_epi16(score_matrix.bias())); + data_ = _mm_and_si128(_mm_or_si128(s1, s2), _mm_set1_epi16(255)); + data_ = _mm_subs_epi16(data_, _mm_set1_epi16(score_matrix.bias())); } #endif @@ -251,7 +275,7 @@ struct ScoreVector } ScoreVector& operator++() { - data_ = _mm_adds_epi16(data_, ::SIMD::_mm_set1_epi16(1)); + data_ = _mm_adds_epi16(data_, _mm_set1_epi16(1)); return *this; } @@ -259,8 +283,13 @@ struct ScoreVector return ScoreVector(_mm_cmpeq_epi16(data_, v.data_)); } - friend uint32_t cmp_mask(const ScoreVector&v, const ScoreVector&w) { - return (uint32_t)_mm_movemask_epi8(_mm_cmpeq_epi16(v.data_, w.data_)); + ScoreVector operator>(const ScoreVector& v) const { + return ScoreVector(_mm_cmpgt_epi16(data_, v.data_)); + } + + template + ScoreVector shift_left() const { + return ScoreVector(_mm_slli_si128(data_, bytes)); } ScoreVector& max(const ScoreVector&rhs) @@ -274,21 +303,16 @@ struct ScoreVector return ScoreVector(_mm_max_epi16(lhs.data_, rhs.data_)); } - friend ScoreVector blend(const ScoreVector&v, const ScoreVector&w, const ScoreVector&mask) { -#ifdef __SSE4_1__ - return ScoreVector(_mm_blendv_epi8(v.data_, w.data_, mask.data_)); -#else - __m128i a = _mm_andnot_si128(mask.data_, v.data_); - __m128i b = _mm_and_si128(mask.data_, w.data_); - return ScoreVector(_mm_or_si128(a, b)); -#endif - } - void store(int16_t *ptr) const { _mm_storeu_si128((__m128i*)ptr, data_); } + void store_aligned(int16_t* ptr) const + { + _mm_store_si128((__m128i*)ptr, data_); + } + int16_t operator[](int i) const { int16_t d[8]; store(d); @@ -318,10 +342,36 @@ struct ScoreVector return s; } + static ScoreVector load_aligned(const int16_t* x) { + return ScoreVector(_mm_load_si128((const __m128i*)x)); + } + __m128i data_; }; +template +static inline uint32_t cmp_mask(const ScoreVector& v, const ScoreVector& w) { + return (uint32_t)_mm_movemask_epi8(_mm_cmpeq_epi16(v.data_, w.data_)); +} + +template +static inline ScoreVector blend(const ScoreVector& v, const ScoreVector& w, const ScoreVector& mask) { +#ifdef __SSE4_1__ + return ScoreVector(_mm_blendv_epi8(v.data_, w.data_, mask.data_)); +#else + __m128i a = _mm_andnot_si128(mask.data_, v.data_); + __m128i b = _mm_and_si128(mask.data_, w.data_); + return ScoreVector(_mm_or_si128(a, b)); +#endif +} + +template +static inline int16_t extract(ScoreVector sv) { + return 0; +} + + #endif #ifdef __SSE2__ @@ -403,8 +453,8 @@ static inline int16_t extract_channel(const DISPATCH_ARCH::ScoreVector -static inline DISPATCH_ARCH::ScoreVector set_channel(const DISPATCH_ARCH::ScoreVector& v, const int i, const int16_t x) { - return DISPATCH_ARCH::ScoreVector(v).set(i, x); +static inline void set_channel(DISPATCH_ARCH::ScoreVector& v, const int i, const int16_t x) { + v.set(i, x); } #endif \ No newline at end of file diff --git a/src/dp/score_vector_int8.h b/src/dp/score_vector_int8.h index dc2bb1c3c..b1d18ef22 100644 --- a/src/dp/score_vector_int8.h +++ b/src/dp/score_vector_int8.h @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2022 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -25,14 +25,197 @@ along with this program. If not, see . namespace DISPATCH_ARCH { -#if ARCH_ID == 2 +#if ARCH_ID == 3 template struct ScoreVector { ScoreVector() : - data_(::SIMD::_mm256_set1_epi8(DELTA)) + data_(_mm512_set1_epi8(DELTA)) + {} + + explicit ScoreVector(__m512i data) : + data_(data) + {} + + explicit ScoreVector(int8_t x) : + data_(_mm512_set1_epi8(x)) + {} + + explicit ScoreVector(int x) : + data_(_mm512_set1_epi8(x)) + {} + + explicit ScoreVector(const int8_t* s) : + data_(_mm512_loadu_si512(reinterpret_cast(s))) + { } + + explicit ScoreVector(const uint8_t* s) : + data_(_mm512_loadu_si512(reinterpret_cast(s))) + { } + + ScoreVector(unsigned a, __m512i seq) + { + /*const __m256i* row_lo = reinterpret_cast(&score_matrix.matrix8_low()[a << 5]); + const __m256i* row_hi = reinterpret_cast(&score_matrix.matrix8_high()[a << 5]); + + __m256i high_mask = _mm256_slli_epi16(_mm256_and_si256(seq, _mm256_set1_epi8('\x10')), 3); + __m256i seq_low = _mm256_or_si256(seq, high_mask); + __m256i seq_high = _mm256_or_si256(seq, _mm256_xor_si256(high_mask, _mm256_set1_epi8('\x80'))); + + __m256i r1 = _mm512_load_si512(row_lo); + __m256i r2 = _mm512_load_si512(row_hi); + + __m256i s1 = _mm256_shuffle_epi8(r1, seq_low); + __m256i s2 = _mm256_shuffle_epi8(r2, seq_high); + data_ = _mm256_or_si256(s1, s2);*/ + } + + ScoreVector operator+(const ScoreVector& rhs) const + { + return ScoreVector(_mm512_adds_epi8(data_, rhs.data_)); + } + + ScoreVector operator-(const ScoreVector& rhs) const + { + return ScoreVector(_mm512_subs_epi8(data_, rhs.data_)); + } + + ScoreVector& operator+=(const ScoreVector& rhs) { + data_ = _mm512_adds_epi8(data_, rhs.data_); + return *this; + } + + ScoreVector& operator-=(const ScoreVector& rhs) + { + data_ = _mm512_subs_epi8(data_, rhs.data_); + return *this; + } + + ScoreVector& operator &=(const ScoreVector& rhs) { + data_ = _mm512_and_si512(data_, rhs.data_); + return *this; + } + + ScoreVector& operator++() { + data_ = _mm512_adds_epi8(data_, _mm512_set1_epi8(1)); + return *this; + } + + friend ScoreVector blend(const ScoreVector&v, const ScoreVector&w, const ScoreVector&mask) { + return ScoreVector(); // (_mm256_blendv_epi8(v.data_, w.data_, mask.data_)); + } + + ScoreVector operator==(const ScoreVector&v) const { + return ScoreVector(); // ScoreVector(_mm256_cmpeq_epi8(data_, v.data_)); + } + + friend uint32_t cmp_mask(const ScoreVector&v, const ScoreVector&w) { + return 0; // (uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v.data_, w.data_)); + } + + int operator [](unsigned i) const + { + return *(((uint8_t*)&data_) + i); + } + + ScoreVector& set(unsigned i, uint8_t v) + { + *(((uint8_t*)&data_) + i) = v; + return *this; + } + + ScoreVector& max(const ScoreVector& rhs) + { + data_ = _mm512_max_epi8(data_, rhs.data_); + return *this; + } + + ScoreVector& min(const ScoreVector& rhs) + { + data_ = _mm512_min_epi8(data_, rhs.data_); + return *this; + } + + friend ScoreVector max(const ScoreVector& lhs, const ScoreVector& rhs) + { + return ScoreVector(_mm512_max_epi8(lhs.data_, rhs.data_)); + } + + friend ScoreVector min(const ScoreVector& lhs, const ScoreVector& rhs) + { + return ScoreVector(_mm512_min_epi8(lhs.data_, rhs.data_)); + } + + void store(int8_t* ptr) const + { + _mm512_storeu_si512((__m512i*)ptr, data_); + } + + friend std::ostream& operator<<(std::ostream& s, ScoreVector v) + { + int8_t x[32]; + v.store(x); + for (unsigned i = 0; i < 32; ++i) + printf("%3i ", (int)x[i]); + return s; + } + + void expand_from_8bit() {} + + __m512i data_; + +}; + +template +struct ScoreTraits> +{ + enum { CHANNELS = 64 }; + typedef ::DISPATCH_ARCH::SIMD::Vector Vector; + typedef int8_t Score; + typedef uint8_t Unsigned; + typedef uint32_t Mask; + struct TraceMask { + static uint64_t make(uint32_t vmask, uint32_t hmask) { + return (uint64_t)vmask << 32 | (uint64_t)hmask; + } + static uint64_t vmask(int channel) { + return (uint64_t)1 << (channel + 32); + } + static uint64_t hmask(int channel) { + return (uint64_t)1 << channel; + } + uint64_t gap; + uint64_t open; + }; + static ScoreVector zero() { + return ScoreVector(); + } + static constexpr int8_t max_score() { + return SCHAR_MAX; + } + static int int_score(int8_t s) + { + return (int)s - DELTA; + } + static constexpr int max_int_score() { + return SCHAR_MAX - DELTA; + } + static constexpr int8_t zero_score() { + return DELTA; + } + static void saturate(ScoreVector& v) {} +}; + +#elif ARCH_ID == 2 + +template +struct ScoreVector +{ + + ScoreVector() : + data_(_mm256_set1_epi8(DELTA)) {} explicit ScoreVector(__m256i data) : @@ -40,11 +223,11 @@ struct ScoreVector {} explicit ScoreVector(int8_t x) : - data_(::SIMD::_mm256_set1_epi8(x)) + data_(_mm256_set1_epi8(x)) {} explicit ScoreVector(int x) : - data_(::SIMD::_mm256_set1_epi8(x)) + data_(_mm256_set1_epi8(x)) {} explicit ScoreVector(const int8_t* s) : @@ -60,9 +243,11 @@ struct ScoreVector const __m256i* row_lo = reinterpret_cast(&score_matrix.matrix8_low()[a << 5]); const __m256i* row_hi = reinterpret_cast(&score_matrix.matrix8_high()[a << 5]); - __m256i high_mask = _mm256_slli_epi16(_mm256_and_si256(seq, ::SIMD::_mm256_set1_epi8('\x10')), 3); + seq = letter_mask(seq); + + __m256i high_mask = _mm256_slli_epi16(_mm256_and_si256(seq, _mm256_set1_epi8('\x10')), 3); __m256i seq_low = _mm256_or_si256(seq, high_mask); - __m256i seq_high = _mm256_or_si256(seq, _mm256_xor_si256(high_mask, ::SIMD::_mm256_set1_epi8('\x80'))); + __m256i seq_high = _mm256_or_si256(seq, _mm256_xor_si256(high_mask, _mm256_set1_epi8('\x80'))); __m256i r1 = _mm256_load_si256(row_lo); __m256i r2 = _mm256_load_si256(row_hi); @@ -99,7 +284,7 @@ struct ScoreVector } ScoreVector& operator++() { - data_ = _mm256_adds_epi8(data_, ::SIMD::_mm256_set1_epi8(1)); + data_ = _mm256_adds_epi8(data_, _mm256_set1_epi8(1)); return *this; } @@ -111,18 +296,27 @@ struct ScoreVector return ScoreVector(_mm256_cmpeq_epi8(data_, v.data_)); } + ScoreVector operator>(const ScoreVector& v) const { + return ScoreVector(_mm256_cmpgt_epi8(data_, v.data_)); + } + friend uint32_t cmp_mask(const ScoreVector&v, const ScoreVector&w) { return (uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v.data_, w.data_)); } int operator [](unsigned i) const { - return *(((uint8_t*)&data_) + i); + return *(((int8_t*)&data_) + i); } - ScoreVector& set(unsigned i, uint8_t v) + ScoreVector& set(unsigned i, int8_t v) { - *(((uint8_t*)&data_) + i) = v; + //*(((uint8_t*)&data_) + i) = v; + //data_ = _mm256_insert_epi8(data_, v, i); + alignas(32) std::array s; + _mm256_store_si256((__m256i *)s.data(), data_); + s[i] = v; + data_ = _mm256_load_si256((__m256i*)s.data()); return *this; } @@ -153,6 +347,11 @@ struct ScoreVector _mm256_storeu_si256((__m256i*)ptr, data_); } + void store_aligned(int8_t* ptr) const + { + _mm256_store_si256((__m256i*)ptr, data_); + } + friend std::ostream& operator<<(std::ostream& s, ScoreVector v) { int8_t x[32]; @@ -162,12 +361,39 @@ struct ScoreVector return s; } + static ScoreVector load_aligned(const int8_t* x) { + return ScoreVector(_mm256_load_si256((const __m256i*)x)); + } + void expand_from_8bit() {} __m256i data_; }; +template +static inline int8_t extract(ScoreVector sv) { + return (int8_t)_mm256_extract_epi8(sv.data_, i); +} + +template +static inline void store_expanded(ScoreVector sv, int16_t* dst) { + const __m256i z = _mm256_setzero_si256(); + const __m256i a = _mm256_permute4x64_epi64(sv.data_, 216); + __m256i b = _mm256_unpacklo_epi8(a, z); + __m256i c = _mm256_slli_si256(_mm256_cmpgt_epi8(z, b), 1); + _mm256_store_si256((__m256i*)dst, _mm256_or_si256(b, c)); + + b = _mm256_unpackhi_epi8(a, z); + c = _mm256_slli_si256(_mm256_cmpgt_epi8(z, b), 1); + _mm256_store_si256((__m256i*)(dst + 16), _mm256_or_si256(b, c)); +} + +template +static inline void store_expanded(ScoreVector sv, int8_t* dst) { + _mm256_store_si256((__m256i*)dst, sv.data_); +} + template struct ScoreTraits> { @@ -215,7 +441,7 @@ struct ScoreVector { ScoreVector(): - data_(::SIMD::_mm_set1_epi8(DELTA)) + data_(_mm_set1_epi8(DELTA)) {} explicit ScoreVector(__m128i data): @@ -223,11 +449,11 @@ struct ScoreVector {} explicit ScoreVector(int8_t x): - data_(::SIMD::_mm_set1_epi8(x)) + data_(_mm_set1_epi8(x)) {} explicit ScoreVector(int x): - data_(::SIMD::_mm_set1_epi8(x)) + data_(_mm_set1_epi8(x)) {} explicit ScoreVector(const int8_t* s) : @@ -243,9 +469,11 @@ struct ScoreVector { const __m128i* row = reinterpret_cast(&score_matrix.matrix8()[a << 5]); - __m128i high_mask = _mm_slli_epi16(_mm_and_si128(seq, ::SIMD::_mm_set1_epi8('\x10')), 3); + seq = letter_mask(seq); + + __m128i high_mask = _mm_slli_epi16(_mm_and_si128(seq, _mm_set1_epi8('\x10')), 3); __m128i seq_low = _mm_or_si128(seq, high_mask); - __m128i seq_high = _mm_or_si128(seq, _mm_xor_si128(high_mask, ::SIMD::_mm_set1_epi8('\x80'))); + __m128i seq_high = _mm_or_si128(seq, _mm_xor_si128(high_mask, _mm_set1_epi8('\x80'))); __m128i r1 = _mm_load_si128(row); __m128i r2 = _mm_load_si128(row + 1); @@ -282,10 +510,14 @@ struct ScoreVector } ScoreVector& operator++() { - data_ = _mm_adds_epi8(data_, ::SIMD::_mm_set1_epi8(1)); + data_ = _mm_adds_epi8(data_, _mm_set1_epi8(1)); return *this; } + ScoreVector shift_left(int bytes) { + return _mm_slli_si128(data_, bytes); + } + friend ScoreVector blend(const ScoreVector&v, const ScoreVector&w, const ScoreVector&mask) { return ScoreVector(_mm_blendv_epi8(v.data_, w.data_, mask.data_)); } @@ -294,6 +526,10 @@ struct ScoreVector return ScoreVector(_mm_cmpeq_epi8(data_, v.data_)); } + ScoreVector operator>(const ScoreVector& v) const { + return ScoreVector(_mm_cmpgt_epi8(data_, v.data_)); + } + friend uint32_t cmp_mask(const ScoreVector&v, const ScoreVector&w) { return _mm_movemask_epi8(_mm_cmpeq_epi8(v.data_, w.data_)); } @@ -336,6 +572,11 @@ struct ScoreVector _mm_storeu_si128((__m128i*)ptr, data_); } + void store_aligned(int8_t* ptr) const + { + _mm_store_si128((__m128i*)ptr, data_); + } + friend std::ostream& operator<<(std::ostream &s, ScoreVector v) { int8_t x[16]; @@ -345,12 +586,21 @@ struct ScoreVector return s; } + static ScoreVector load_aligned(const int8_t* x) { + return ScoreVector(_mm_load_si128((const __m128i*)x)); + } + void expand_from_8bit() {} __m128i data_; }; +template +static inline int8_t extract(ScoreVector sv) { + return 0; +} + template struct ScoreTraits> { @@ -403,8 +653,9 @@ static inline int8_t extract_channel(const DISPATCH_ARCH::ScoreVector -static inline DISPATCH_ARCH::ScoreVector set_channel(const DISPATCH_ARCH::ScoreVector& v, const int i, const int8_t x) { - return DISPATCH_ARCH::ScoreVector(v).set(i, x); +static inline void set_channel(DISPATCH_ARCH::ScoreVector& v, const int i, const int8_t x) { + v.set(i, x); } + #endif \ No newline at end of file diff --git a/src/dp/swipe/anchored.h b/src/dp/swipe/anchored.h new file mode 100644 index 000000000..fbc781aa8 --- /dev/null +++ b/src/dp/swipe/anchored.h @@ -0,0 +1,246 @@ +#pragma once +#include +#include "../../basic/sequence.h" +#include "../score_vector.h" +#include "../../util/simd/transpose32x32.h" +#include "../../util/simd/transpose16x16.h" +#include "banded_matrix.h" +#include "cell_update.h" +#include "config.h" +#include "../../util/geo/geo.h" +#include "../score_vector_int8.h" +#include "../score_vector_int16.h" +#include "../../util/util.h" +#include "../../util/data_structures/array.h" + +using std::copy; +using std::array; +using std::numeric_limits; +using std::vector; +using std::pair; +using std::tie; + +namespace DP { namespace AnchoredSwipe { + +struct Options { + const int16_t* const* profile, * const* profile_rev; +}; + +#if ARCH_ID == 2 + +namespace DISPATCH_ARCH { + +static char blank[64] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static constexpr Loc L = 13; + +template +pair limits(const Target* targets, size_t count) { + int band = 0, target_len = 0; + for (const Target* i = targets; i < targets + count; ++i) { + assert(i->band() > 0); + band = std::max(band, i->band()); + target_len = std::max(target_len, i->seq.length()); + } + return { band,target_len }; +} + +template +struct TargetIterator { + enum { CHANNELS = ::DISPATCH_ARCH::ScoreTraits::CHANNELS }; + using Score = typename ::DISPATCH_ARCH::ScoreTraits::Score; + TargetIterator(Target* targets, int64_t target_count, Loc target_len_max, DP::BandedSwipe::DISPATCH_ARCH::Matrix& matrix, const Options& options) : + options(options), + begin(targets), + next(targets), + end(targets + target_count), + active(0), + band(0) + { + while (active < CHANNELS && next < end) { + int i = active; + target_seqs[i] = Array(target_len_max + 32 + 1); + init_target(i); + matrix.init_channel_diag(i, -Geo::i(0, targets[i].d_begin)); + } + for (int i = active; i < CHANNELS; ++i) + reset_channel(i); + band = round_up(band, (Loc)CHANNELS); + //matrix.init_channels_nw(-Geo::i(0, targets[0].d_begin), score_matrix.gap_open(), score_matrix.gap_extend()); + } + inline bool init_target(int channel) { + while (next->band() <= 0 && next < end) ++next; + if (next == end) + return false; + target_idx[channel] = int(next - begin); + targets[channel] = *next++; + loc[channel] = 0; + target_seqs[channel].assign(MASK_LETTER); + if (targets[channel].reverse) + target_seqs[channel].push_back_reversed(targets[channel].seq.data(), targets[channel].seq.end()); + else + target_seqs[channel].push_back(targets[channel].seq.data(), targets[channel].seq.end()); + target_seqs[channel].push_back(32, MASK_LETTER); + for (int j = 0; j < AMINO_ACID_COUNT; ++j) + profile_ptrs[channel][j] = (const Score*)(targets[channel].reverse ? options.profile_rev[j] : options.profile[j]) + + targets[channel].query_start + Geo::i(0, targets[channel].d_begin) - 1; + ++active; + band = std::max(band, targets[channel].band()); + return true; + } + inline void init_target_matrix(int channel, DP::BandedSwipe::DISPATCH_ARCH::Matrix& matrix, Sv& max_score, Sv& col_counter, Sv& max_j) { + matrix.init_channel_nw(channel, -Geo::i(0, targets[channel].d_begin), score_matrix.gap_open(), score_matrix.gap_extend()); + set_channel(max_score, channel, -1); + set_channel(col_counter, channel, 0); + set_channel(max_j, channel, -1); + } + inline void reset_channel(int channel) { + for (int j = 0; j < AMINO_ACID_COUNT; ++j) + profile_ptrs[channel][j] = (const Score*)options.profile[0]; + } + inline void next_block(DP::BandedSwipe::DISPATCH_ARCH::Matrix& matrix, Sv& max_score, Sv& max_i, Sv& max_j, Sv& col_counter) { + for (int i = 0; i < CHANNELS; ++i) { + if (targets[i].blank()) { + std::fill(letters[i].begin(), letters[i].end(), MASK_LETTER); + continue; + } + if (loc[i] >= targets[i].seq.length() + 1) { + const ::Score score = max_score[i]; + if (score >= 0) { + begin[target_idx[i]].score = score + 1; + const Score j1 = max_j[i]; + if (j1 < numeric_limits::max()) { + begin[target_idx[i]].target_end = (Loc)j1 + 1 - 1; + begin[target_idx[i]].query_end = Geo::i((Loc)j1, targets[i].d_begin) + (Loc)max_i[i] + 1 - 1; + assert(begin[target_idx[i]].target_end > 0 && begin[target_idx[i]].query_end > 0); + } + else + begin[target_idx[i]].score = numeric_limits::max(); + } + --active; + targets[i].reset(); + if (next < end) { + if (!init_target(i)) { + std::fill(letters[i].begin(), letters[i].end(), MASK_LETTER); + reset_channel(i); + continue; + } + init_target_matrix(i, matrix, max_score, col_counter, max_j); + band = round_up(band, (Loc)CHANNELS); + } + else { + std::fill(letters[i].begin(), letters[i].end(), MASK_LETTER); + reset_channel(i); + continue; + } + } + copy(target_seqs[i].data() + loc[i], target_seqs[i].data() + loc[i] + L, letters[i].data()); + loc[i] += L; + for (int j = 0; j < AMINO_ACID_COUNT; ++j) + profile_ptrs[i][j] += L; + } + } + inline array column_ptrs(int k) { + array prof_ptr; + for (int i = 0; i < CHANNELS; ++i) { + const Letter l = letter_mask(letters[i][k + L]); + prof_ptr[i] = profile_ptrs[i][(int)l] + k; + } + return prof_ptr; + } + size_t net_cells(int k) const { + size_t n = 0; + for (int i = 0; i < CHANNELS; ++i) + if (!targets[i].blank() && (loc[i] + k < targets[i].seq.length())) { + int j = loc[i] + k, i0 = std::max(Geo::i(j, targets[i].d_begin), 0), + i1 = std::min(Geo::i(j, targets[i].d_end), targets[i].query_length); + //assert(i1 - i0 >= 0); + n += std::max(i1 - i0, 0); + } + return n; + } + const Options& options; + array, CHANNELS> targets; + array, CHANNELS> target_seqs; + Target* begin, * next, * end; + int active; + array, CHANNELS> profile_ptrs; + array loc; + array, CHANNELS> letters; + array padding; + array target_idx; + Loc band; +}; + +template +Stats FLATTEN smith_waterman(DP::AnchoredSwipe::Target::Score>* targets, int64_t target_count, const Options& options) { + using Score = typename ::DISPATCH_ARCH::ScoreTraits::Score; + const Loc CHANNELS = ::DISPATCH_ARCH::ScoreTraits::CHANNELS; + constexpr Score SCORE_MIN = numeric_limits::min(); + if (target_count == 0) + return Stats(); + + alignas(32) Score scores[CHANNELS * CHANNELS]; + Loc band_max, target_len_max; + tie(band_max, target_len_max) = limits(targets, target_count); + DP::BandedSwipe::DISPATCH_ARCH::Matrix matrix(round_up(band_max, CHANNELS), 0, Sv(SCORE_MIN)); + assert(round_up(band_max, CHANNELS) <= numeric_limits::max()); + TargetIterator target_it(targets, target_count, target_len_max, matrix, options); + const Sv go = Sv(score_matrix.gap_open() + score_matrix.gap_extend()), + ge = Sv(score_matrix.gap_extend()), one = Sv(1); + Sv max_score(-1), col_counter(0), max_j(-1), max_i(0); + Stats stats; + + while(target_it.next_block(matrix, max_score, max_i, max_j, col_counter), target_it.active > 0) { + const int band = target_it.band; + for (int k = -L; k < 0; ++k) { +#ifdef DP_STAT + stats.gross_cells += (size_t)band * CHANNELS; + stats.net_cells += target_it.net_cells(k); +#endif + + typename DP::BandedSwipe::DISPATCH_ARCH::Matrix::ColumnIterator it(matrix.begin(0, 0)); + array prof_ptr = target_it.column_ptrs(k); + Sv vgap = Sv(SCORE_MIN), hgap = Sv(), col_best = Sv(SCORE_MIN), row_counter(0), col_max_i(0); + + for (int i = 0; i < band;) { + transpose_offset(prof_ptr.data(), CHANNELS, i/CHANNELS, scores, __m256i()); + const Score* score_ptr = scores; + + do { + hgap = it.hgap(); + Sv match_scores(score_ptr); + Sv score = it.diag() + match_scores; + score = max(score, hgap); + score = max(score, vgap); + Sv open = score - go; + const Sv gt_mask = score > col_best; + col_max_i = blend(col_max_i, row_counter, gt_mask); + row_counter += one; + col_best = max(col_best, score); + vgap -= ge; + hgap -= ge; + vgap = max(vgap, open); + hgap = max(hgap, open); + it.set_hgap(hgap); + it.set_score(score); + ++it; + score_ptr += CHANNELS; + ++i; + } while ((i & (CHANNELS - 1)) != 0); + } + const Sv gt_mask = col_best > max_score; + max_j = blend(max_j, col_counter, gt_mask); + max_i = blend(max_i, col_max_i, gt_mask); + max_score = max(max_score, col_best); + col_counter += one; + } + } + return stats; +} + +} + +#endif + +}} + diff --git a/src/dp/swipe/anchored_wrapper.cpp b/src/dp/swipe/anchored_wrapper.cpp new file mode 100644 index 000000000..2a2cb242b --- /dev/null +++ b/src/dp/swipe/anchored_wrapper.cpp @@ -0,0 +1,236 @@ +#include +#include "../dp.h" +#include "anchored.h" +#include "../score_profile.h" +#include "../score_vector_int8.h" +#include "../score_vector_int16.h" +#include "../dp/ungapped.h" + +using std::list; +using std::vector; +using std::unique_ptr; +using std::pair; +using std::sort; +using std::runtime_error; +using std::accumulate; + +namespace DP { namespace BandedSwipe { namespace DISPATCH_ARCH { + +struct TargetVector { + vector> int8; + vector> int16; +}; + +struct Profiles { + struct Reverse {}; + Profiles(Sequence seq, const int8_t* cbs, int64_t padding) + { + int16 = make_profile16(seq, cbs, padding); + } + Profiles(const Profiles& p, Reverse): + int16(p.int16.reverse()) + {} + LongScoreProfile int8; + LongScoreProfile int16; +}; + +static Loc get_band() { + return config.sensitivity >= Sensitivity::ULTRA_SENSITIVE ? 160 : (config.sensitivity >= Sensitivity::MORE_SENSITIVE ? 96 : 32); +} + +static void align_right(Sequence target_seq, bool reverse, Loc i, Loc j, Loc d_begin, Loc d_end, Score prefix_score, TargetVector& targets, + int64_t target_idx, + const DP::AnchoredSwipe::Config& cfg) +{ + Loc qlen = cfg.query.length() - i, tlen = target_seq.length(); + const int band_cap = std::max(std::min(qlen / 2, tlen / 2), 1); + const int band = std::min(std::max(get_band(), Loc((d_end - d_begin) * 0.15)), band_cap); + d_begin -= band; + d_end += band - 1; + const Loc d0 = Geo::clip_diag(Geo::diag_sub_matrix(d_begin, i, j), qlen, tlen), + d1 = Geo::clip_diag(Geo::diag_sub_matrix(d_end, i, j), qlen, tlen); + tlen = std::min(tlen, Geo::j(qlen - 1, d0) + 1); + assert(tlen > 0); + assert(d1 >= d0); + + const Sequence clipped_target = reverse ? target_seq.subseq(target_seq.length() - tlen, target_seq.length()) : target_seq.subseq(0, tlen); + + auto& t = targets.int16; + t.emplace_back(clipped_target, d0, d1 + 1, i, qlen, target_idx, reverse); +} + +static void align_left(Sequence target_seq, Loc i, Loc j, Loc d_begin, Loc d_end, Score suffix_score, TargetVector& targets, + int64_t target_idx, + const DP::AnchoredSwipe::Config& cfg) +{ + const Loc qlen = cfg.query.length(), tlen = target_seq.length(); + const Loc ir = qlen - 1 - i, jr = tlen - 1 - j; + align_right(target_seq.subseq(0, j + 1), true, ir, jr, Geo::rev_diag(d_end, qlen, tlen), Geo::rev_diag(d_begin, qlen, tlen), suffix_score, targets, target_idx, cfg); +} + +static void add_target(DpTarget& t, TargetVector& targets, + int64_t& target_idx, + const DP::AnchoredSwipe::Config& cfg) +{ + //t.anchor = make_clipped_anchor(t.anchor, cfg.query, cfg.query_cbs, t.seq); + //if (t.anchor.score == 0) + //return; + if (t.extend_right(cfg.query.length())) { + const Loc i = t.anchor.query_end(), j = t.anchor.subject_end(); + align_right(t.seq.subseq(j), false, i, j, t.anchor.d_min_right, t.anchor.d_max_right, t.anchor.prefix_score, targets, target_idx, cfg); + ++target_idx; + } + if (t.extend_left()) { + const Score suffix_score = cfg.score_hint - t.anchor.prefix_score + t.anchor.score; + align_left(t.seq, t.anchor.query_begin() - 1, t.anchor.subject_begin() - 1, t.anchor.d_min_left, t.anchor.d_max_left, suffix_score, + targets, target_idx, cfg); + ++target_idx; + } +} + +static void swipe_threads(DP::AnchoredSwipe::Target* targets, int64_t count, const DP::AnchoredSwipe::Options& options, const DP::AnchoredSwipe::Config& cfg) { + using Target = DP::AnchoredSwipe::Target; + ThreadPool::TaskSet task_set(*cfg.thread_pool, 0); + int64_t size = 0; + Target* i0 = targets, *i1 = targets, *end = targets + count; + while (i1 < end) { + const auto n = std::min((ptrdiff_t)16, end - i1); + size += accumulate(i1, i1 + n, (int64_t)0, [](int64_t n, const Target& t) {return n + t.gross_cells(); }); + i1 += n; + if (size >= config.swipe_task_size) { +#if ARCH_ID == 2 + task_set.enqueue(DP::AnchoredSwipe::DISPATCH_ARCH::smith_waterman<::DISPATCH_ARCH::ScoreVector>, i0, i1 - i0, options); +#endif + cfg.stats.inc(Statistics::SWIPE_TASKS_TOTAL); + cfg.stats.inc(Statistics::SWIPE_TASKS_ASYNC); + i0 = i1; + size = 0; + } + } + if (task_set.total() == 0) { + cfg.stats.inc(Statistics::SWIPE_TASKS_TOTAL); +#if ARCH_ID == 2 + DP::AnchoredSwipe::DISPATCH_ARCH::smith_waterman<::DISPATCH_ARCH::ScoreVector>(i0, i1 - i0, options); +#endif + return; + } + if (i1 - i0 > 0) { + cfg.stats.inc(Statistics::SWIPE_TASKS_TOTAL); + cfg.stats.inc(Statistics::SWIPE_TASKS_ASYNC); +#if ARCH_ID == 2 + task_set.enqueue(DP::AnchoredSwipe::DISPATCH_ARCH::smith_waterman<::DISPATCH_ARCH::ScoreVector>, i0, i1 - i0, options); +#endif + } + task_set.run(); +} + +list anchored_swipe(Targets& targets, const DP::AnchoredSwipe::Config& cfg) { + task_timer total; + + TargetVector target_vec; + int64_t target_count = 0, target_len = 0; + Loc max_target_len = 0; + for (int bin = 0; bin < DP::BINS; ++bin) + for (const DpTarget& t : targets[bin]) { + ++target_count; + target_len += t.seq.length(); + max_target_len = std::max(max_target_len, t.seq.length()); + } + + task_timer timer; + //target_vec.int8.reserve(target_count * 2); + target_vec.int16.reserve(target_count * 2); + cfg.stats.inc(Statistics::TIME_ANCHORED_SWIPE_ALLOC, timer.microseconds()); + + timer.go(); + Profiles profiles(cfg.query, cfg.query_cbs, cfg.query.length() + max_target_len + 32), + profiles_rev(profiles, Profiles::Reverse()); + const auto prof_pointers = profiles.int16.pointers(0), prof_pointers_rev = profiles_rev.int16.pointers(0); + cfg.stats.inc(Statistics::TIME_PROFILE, timer.microseconds()); + + timer.go(); + int64_t target_idx = 0; + for (int bin = 0; bin < DP::BINS; ++bin) + for (DpTarget& t : targets[bin]) { + add_target(t, target_vec, target_idx, cfg); + } + cfg.stats.inc(Statistics::TIME_ANCHORED_SWIPE_ADD, timer.microseconds()); + + auto& t = target_vec.int16; + + timer.go(); + sort(target_vec.int8.begin(), target_vec.int8.end()); + sort(target_vec.int16.begin(), target_vec.int16.end()); + cfg.stats.inc(Statistics::TIME_ANCHORED_SWIPE_SORT, timer.microseconds()); + + DP::AnchoredSwipe::Stats stats; + DP::AnchoredSwipe::Options options{ prof_pointers.data(), prof_pointers_rev.data() }; + + timer.go(); +#ifdef __SSE4_1__ + //DP::AnchoredSwipe::DISPATCH_ARCH::smith_waterman<::DISPATCH_ARCH::ScoreVector>(target_vec.int8.data(), target_vec.int8.size()); + //stats = DP::AnchoredSwipe::DISPATCH_ARCH::smith_waterman<::DISPATCH_ARCH::ScoreVector>(target_vec.int16.data(), target_vec.int16.size(), options); + swipe_threads(target_vec.int16.data(), target_vec.int16.size(), options, cfg); + //cfg.stats.inc(Statistics::GROSS_DP_CELLS, stats.gross_cells); + //cfg.stats.inc(Statistics::NET_DP_CELLS, stats.net_cells); +#endif + cfg.stats.inc(Statistics::TIME_SW, timer.microseconds()); + + timer.go(); + sort(target_vec.int8.begin(), target_vec.int8.end(), DP::AnchoredSwipe::Target::cmp_target_idx); + sort(target_vec.int16.begin(), target_vec.int16.end(), DP::AnchoredSwipe::Target::cmp_target_idx); + cfg.stats.inc(Statistics::TIME_ANCHORED_SWIPE_SORT, timer.microseconds()); + + timer.go(); + auto target_it = target_vec.int16.cbegin(); + list out; + for (int bin = 0; bin < DP::BINS; ++bin) + for (const DpTarget& t : targets[bin]) { + if (t.anchor.score == 0) + continue; + int score = t.anchor.score, i0 = t.anchor.query_begin(), i1 = t.anchor.query_end(), j0 = t.anchor.subject_begin(), j1 = t.anchor.subject_end(); + int64_t gross_cells = 0, net_cells = 0; + if (t.extend_right(cfg.query.length())) { + score += target_it->score; + i1 += target_it->query_end; + j1 += target_it->target_end; +#ifdef DP_STAT + auto c = target_it->cells(); + gross_cells += c.first; + net_cells += c.second; +#endif + ++target_it; + } + if (t.extend_left()) { + score += target_it->score; + i0 -= target_it->query_end; + j0 -= target_it->target_end; +#ifdef DP_STAT + auto c = target_it->cells(); + gross_cells += c.first; + net_cells += c.second; +#endif + ++target_it; + } + if (std::max(double(i1 - i0) / cfg.query.length(), double(j1 - j0) / t.seq.length()) * 100.0 < config.query_or_target_cover) + continue; + const double evalue = score_matrix.evalue(score, cfg.query.length(), t.seq.length()); + if (evalue <= config.max_evalue) { + out.emplace_back(); + out.back().score = score; + out.back().evalue = evalue; + out.back().bit_score = score_matrix.bitscore(score); + out.back().swipe_target = t.target_idx; + out.back().query_range = { i0,i1 }; + out.back().query_source_range = out.back().query_range; + out.back().subject_range = { j0,j1 }; + out.back().reserved1 = (int)gross_cells; // stats.gross_cells; + out.back().reserved2 = (int)net_cells; // stats.net_cells; + } + } + cfg.stats.inc(Statistics::TIME_ANCHORED_SWIPE_OUTPUT, timer.microseconds()); + cfg.stats.inc(Statistics::TIME_ANCHORED_SWIPE, total.microseconds()); + return out; +} + +}}} \ No newline at end of file diff --git a/src/dp/swipe/banded_3frame_swipe.cpp b/src/dp/swipe/banded_3frame_swipe.cpp index e4e5febcd..3d5c32ac6 100644 --- a/src/dp/swipe/banded_3frame_swipe.cpp +++ b/src/dp/swipe/banded_3frame_swipe.cpp @@ -31,6 +31,7 @@ using std::list; using std::thread; using std::atomic; using std::pair; +using std::vector; namespace DISPATCH_ARCH { @@ -391,11 +392,11 @@ Hsp traceback(Sequence *query, Strand strand, int dna_len, const Banded3FrameSwi template list banded_3frame_swipe( const TranslatedSequence &query, - Strand strand, vector::const_iterator subject_begin, - vector::const_iterator subject_end, + Strand strand, std::vector::const_iterator subject_begin, + std::vector::const_iterator subject_end, DpStat &stat, bool parallel, - vector &overflow) + std::vector &overflow) { typedef typename Banded3FrameSwipeMatrixRef<_sv, _traceback>::type Matrix; typedef typename ScoreTraits<_sv>::Score Score; @@ -425,7 +426,9 @@ list banded_3frame_swipe( frameshift_penalty(score_matrix.frame_shift()); SwipeProfile<_sv> profile; +#ifndef __SSSE3__ std::array target_scores; +#endif Score best[ScoreTraits<_sv>::CHANNELS]; int max_col[ScoreTraits<_sv>::CHANNELS]; for (int i = 0; i < ScoreTraits<_sv>::CHANNELS; ++i) { @@ -511,14 +514,14 @@ list banded_3frame_swipe( } template -list banded_3frame_swipe_targets(vector::const_iterator begin, +list banded_3frame_swipe_targets(std::vector::const_iterator begin, vector::const_iterator end, bool score_only, const TranslatedSequence &query, Strand strand, DpStat &stat, bool parallel, - vector &overflow) + std::vector &overflow) { list out; for (vector::const_iterator i = begin; i < end; i += std::min((ptrdiff_t)ScoreTraits<_sv>::CHANNELS, end - i)) { @@ -530,8 +533,8 @@ list banded_3frame_swipe_targets(vector::const_iterator begin, return out; } -void banded_3frame_swipe_worker(vector::const_iterator begin, - vector::const_iterator end, +void banded_3frame_swipe_worker(std::vector::const_iterator begin, + std::vector::const_iterator end, atomic *next, bool score_only, const TranslatedSequence *query, @@ -567,7 +570,7 @@ list banded_3frame_swipe(const TranslatedSequence &query, Strand strand, ve vector*> thread_out; vector> thread_overflow(config.threads_); atomic next(0); - for (size_t i = 0; i < config.threads_; ++i) { + for (int i = 0; i < config.threads_; ++i) { thread_out.push_back(new list); threads.emplace_back(banded_3frame_swipe_worker, target_begin, diff --git a/src/dp/swipe/banded_matrix.h b/src/dp/swipe/banded_matrix.h index a354fda89..3cf534f94 100644 --- a/src/dp/swipe/banded_matrix.h +++ b/src/dp/swipe/banded_matrix.h @@ -27,12 +27,14 @@ using std::pair; namespace DP { namespace BandedSwipe { namespace DISPATCH_ARCH { -template +template struct Matrix { + using Score = typename ::DISPATCH_ARCH::ScoreTraits::Score; + static constexpr int CHANNELS = ::DISPATCH_ARCH::ScoreTraits::CHANNELS; struct ColumnIterator { - ColumnIterator(_sv* hgap_front, _sv* score_front) : + ColumnIterator(Sv* hgap_front, Sv* score_front) : hgap_ptr_(hgap_front), score_ptr_(score_front) { } @@ -40,19 +42,19 @@ struct Matrix { ++hgap_ptr_; ++score_ptr_; } - inline _sv hgap() const + inline Sv hgap() const { return *(hgap_ptr_ + 1); } - inline _sv diag() const + inline Sv diag() const { return *score_ptr_; } - inline void set_hgap(const _sv& x) + inline void set_hgap(const Sv& x) { *hgap_ptr_ = x; } - inline void set_score(const _sv& x) + inline void set_score(const Sv& x) { *score_ptr_ = x; } @@ -67,16 +69,55 @@ struct Matrix } void set_hstat(std::nullptr_t) {} inline void set_zero() {} - _sv *hgap_ptr_, *score_ptr_; + Sv *hgap_ptr_, *score_ptr_; }; - Matrix(int band, size_t cols): + Matrix(int band, size_t cols, Sv init = Sv()) : band_(band) { hgap_.resize(band + 1); score_.resize(band); - std::fill(hgap_.begin(), hgap_.end(), _sv()); - std::fill(score_.begin(), score_.end(), _sv()); - + std::fill(hgap_.begin(), hgap_.end(), init); + std::fill(score_.begin(), score_.end(), init); + + } + void init_channel_diag(int channel, int offset) { + Score* ptr = (Score*)score_.begin(); + ptr[offset * CHANNELS + channel] = 0; + } + void init_channel_nw(int channel, int offset, ::Score gap_open, ::Score gap_extend) { + Score* ptr = (Score*)score_.begin(); + ptr[offset * CHANNELS + channel] = 0; + //Score s = -gap_open; + for (int i = offset - 1; i >= 0; --i) { + //s -= gap_extend; + //ptr[i * CHANNELS + channel] = s; + ptr[i * CHANNELS + channel] = std::numeric_limits::min(); + } + //s = -gap_open; + for (int i = offset + 1; i < (int)score_.size(); ++i) { + //s -= gap_extend; + //ptr[i * CHANNELS + channel] = s; + ptr[i * CHANNELS + channel] = std::numeric_limits::min(); + } + ptr = (Score*)hgap_.begin(); + for (size_t i = 0; i < hgap_.size(); ++i) + ptr[i * CHANNELS + channel] = std::numeric_limits::min(); + } + void init_channels_nw(int offset, ::Score gap_open, ::Score gap_extend) { + Score* ptr = (Score*)score_.begin(); + std::fill(&ptr[offset * CHANNELS], &ptr[(offset+1) * CHANNELS], 0); + Score s = -gap_open; + for (int i = offset - 1; i >= 0; --i) { + s -= gap_extend; + std::fill(&ptr[i * CHANNELS], &ptr[(i + 1) * CHANNELS], s); + } + s = -gap_open; + for (int i = offset + 1; i < (int)score_.size(); ++i) { + s -= gap_extend; + std::fill(&ptr[i * CHANNELS], &ptr[(i + 1) * CHANNELS], s); + } + ptr = (Score*)hgap_.begin(); + std::fill(ptr, ptr + hgap_.size() * CHANNELS, std::numeric_limits::min()); } inline ColumnIterator begin(int offset, int col) { @@ -85,13 +126,13 @@ struct Matrix int band() const { return band_; } - _sv operator[](int i) const { + Sv operator[](int i) const { return score_[i]; } #ifdef __APPLE__ - MemBuffer<_sv> hgap_, score_; + MemBuffer hgap_, score_; #else - static thread_local MemBuffer<_sv> hgap_, score_; + static thread_local MemBuffer hgap_, score_; #endif private: int band_; @@ -319,7 +360,7 @@ struct TracebackVectorMatrix struct TracebackIterator { - TracebackIterator(const TraceMask *mask, size_t band, int i, int j, size_t channel) : + TracebackIterator(const TraceMask *mask, size_t band, int i, int j, const int channel) : band_(band), mask_(mask), channel_mask_vgap(TraceMask::vmask(channel)), @@ -366,7 +407,7 @@ struct TracebackVectorMatrix int i, j; }; - TracebackIterator traceback(size_t col, int i0, int band_i, int j, int query_len, size_t channel) const + TracebackIterator traceback(size_t col, int i0, int band_i, int j, int query_len, const int channel) const { return TracebackIterator(&trace_mask_[col*band_ + band_i], band_, i0 + band_i, j, channel); } @@ -405,10 +446,10 @@ struct TracebackVectorMatrix }; #ifndef __APPLE__ -template thread_local MemBuffer<_sv> Matrix<_sv>::hgap_; -template thread_local MemBuffer<_sv> Matrix<_sv>::score_; -template thread_local MemBuffer<_sv> TracebackVectorMatrix<_sv>::hgap_; -template thread_local MemBuffer<_sv> TracebackVectorMatrix<_sv>::score_; +template thread_local MemBuffer Matrix::hgap_; +template thread_local MemBuffer Matrix::score_; +template thread_local MemBuffer TracebackVectorMatrix::hgap_; +template thread_local MemBuffer TracebackVectorMatrix::score_; #endif template diff --git a/src/dp/swipe/banded_swipe.h b/src/dp/swipe/banded_swipe.h index f63e072fb..a9d20bbc7 100644 --- a/src/dp/swipe/banded_swipe.h +++ b/src/dp/swipe/banded_swipe.h @@ -55,6 +55,7 @@ Hsp traceback(_cbs bias_correction, const TracebackMatrix<_sv> &dp, const DpTarg out.score = ScoreTraits<_sv>::int_score(max_score); out.evalue = evalue; out.bit_score = score_matrix.bitscore(out.score); + out.corrected_bit_score = score_matrix.bitscore_corrected(out.score, p.query.length(), target.true_target_len); out.transcript.reserve(size_t(out.score * config.transcript_len_estimate)); out.matrix = target.matrix; @@ -81,7 +82,9 @@ Hsp traceback(_cbs bias_correction, const TracebackMatrix<_sv> &dp, const DpTarg out.subject_range.begin_ = it.j + 1; out.transcript.reverse(); out.transcript.push_terminator(); + out.target_seq = target.seq; out.query_source_range = TranslatedPosition::absolute_interval(TranslatedPosition(out.query_range.begin_, p.frame), TranslatedPosition(out.query_range.end_, p.frame), p.query_source_len); + out.approx_id = out.approx_id_percent(p.query, target.seq); return out; } @@ -95,6 +98,7 @@ Hsp traceback(_cbs bias_correction, const Matrix &dp, const DpTarget &targ out.score *= config.cbs_matrix_scale; out.evalue = evalue; out.bit_score = score_matrix.bitscore(out.score); + out.corrected_bit_score = score_matrix.bitscore_corrected(out.score, p.query.length(), target.true_target_len); out.frame = p.frame.index(); out.matrix = target.matrix; const int j0 = i1 - (target.d_end - 1), i1_ = i0 + max_col + max_band_i + 1, j1_ = j0 + max_col + 1; @@ -103,6 +107,7 @@ Hsp traceback(_cbs bias_correction, const Matrix &dp, const DpTarget &targ out.d_end = target.d_end; out.query_range.end_ = i1_; out.subject_range.end_ = j1_; + out.target_seq = target.seq; } else { out.d_begin = -target.d_end + (int)p.query.length() - (int)target.seq.length() + 1; @@ -113,8 +118,8 @@ Hsp traceback(_cbs bias_correction, const Matrix &dp, const DpTarget &targ out.length = target.carry_over.len; out.query_range.begin_ = (int)p.query.length() - i1_; out.subject_range.begin_ = (int)target.seq.length() - j1_; + out.approx_id = out.approx_id_percent(Sequence(p.query.reverse()), Sequence(target.seq.reverse())); } - out.target_seq = target.seq; assign_stats(out, stats); out.query_source_range = TranslatedPosition::absolute_interval(TranslatedPosition(out.query_range.begin_, p.frame), TranslatedPosition(out.query_range.end_, p.frame), p.query_source_len); return out; @@ -134,6 +139,7 @@ Hsp traceback(_cbs bias_correction, const TracebackVectorMatrix<_sv> &dp, const out.score = ScoreTraits<_sv>::int_score(max_score); out.evalue = evalue; out.bit_score = score_matrix.bitscore(out.score); + out.corrected_bit_score = score_matrix.bitscore_corrected(out.score, p.query.length(), target.true_target_len); out.transcript.reserve(size_t(out.score * config.transcript_len_estimate)); out.matrix = target.matrix; @@ -173,6 +179,7 @@ Hsp traceback(_cbs bias_correction, const TracebackVectorMatrix<_sv> &dp, const out.transcript.reverse(); out.transcript.push_terminator(); out.query_source_range = TranslatedPosition::absolute_interval(TranslatedPosition(out.query_range.begin_, p.frame), TranslatedPosition(out.query_range.end_, p.frame), p.query_source_len); + out.approx_id = out.approx_id_percent(p.query, target.seq); return out; } template @@ -269,9 +276,10 @@ list swipe(const vector::const_iterator subject_begin, const vect const int i_begin = std::max(i0 + band_parts.begin(part), i0_); const int i_end = std::min(i0 + band_parts.end(part), i1_); const _sv target_mask = load_sv<_sv>(band_parts.mask(part)); + vgap += target_mask; #ifdef DP_STAT - stat.inc(Statistics::GROSS_DP_CELLS, uint64_t(i_end - i_begin) * CHANNELS); - stat.inc(Statistics::NET_DP_CELLS, uint64_t(i_end - i_begin) * popcount64(live & band_parts.bit_mask(part))); + p.stat.inc(Statistics::GROSS_DP_CELLS, uint64_t(i_end - i_begin) * CHANNELS); + p.stat.inc(Statistics::NET_DP_CELLS, uint64_t(i_end - i_begin) * popcount64(live & band_parts.bit_mask(part))); #endif for (int i = i_begin; i < i_end; ++i) { #else @@ -281,6 +289,7 @@ list swipe(const vector::const_iterator subject_begin, const vect auto stat_h = it.hstat(); _sv match_scores = profile.get(p.query[i]); #ifdef STRICT_BAND + hgap += target_mask; match_scores += target_mask; #endif const Cell next = swipe_cell_update(it.diag(), match_scores, cbs_buf(i), extend_penalty, open_penalty, hgap, vgap, col_best, it.trace_mask(), row_counter, IdMask(p.query[i], target_seq)); @@ -327,8 +336,9 @@ list swipe(const vector::const_iterator subject_begin, const vect if (!subject_begin[i].adjusted_matrix()) score *= config.cbs_matrix_scale; const double evalue = score_matrix.evalue(score, qlen, subject_begin[i].true_target_len); - if (score_matrix.report_cutoff(score, evalue)) + if (score > 0 && score_matrix.report_cutoff(score, evalue)) { out.push_back(traceback<_sv>(composition_bias, dp, subject_begin[i], d_begin[i], best[i], evalue, max_col[i], i, i0 - j, i1 - j, max_band_row[i], stats[i], p)); + } } else overflow.push_back(subject_begin[i]); diff --git a/src/dp/swipe/cell_update.h b/src/dp/swipe/cell_update.h index efe18f812..0fc908ec7 100644 --- a/src/dp/swipe/cell_update.h +++ b/src/dp/swipe/cell_update.h @@ -19,6 +19,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ +#pragma once #include "stat_cell.h" template @@ -116,10 +117,12 @@ FORCE_INLINE Cell swipe_cell_update(const Cell& diagonal_cell, { Cell current_cell = diagonal_cell; current_cell += add_cbs(scores, query_bias); + //Sv open_v = current_cell; update_stats(current_cell, horizontal_gap, vertical_gap, id_mask); set_max(current_cell, horizontal_gap); set_max(current_cell, vertical_gap); saturate(current_cell); + //open_v = blend(Sv(), current_cell, open_v == current_cell); make_gap_mask(trace_mask, current_cell, vertical_gap, horizontal_gap); diff --git a/src/dp/swipe/config.h b/src/dp/swipe/config.h new file mode 100644 index 000000000..bba1f5dc0 --- /dev/null +++ b/src/dp/swipe/config.h @@ -0,0 +1,74 @@ +#pragma once +#include +#include "../../basic/sequence.h" +#include "../../util/geo/geo.h" + +namespace DP { namespace AnchoredSwipe { + +struct Stats { + Stats(): + gross_cells(0), + net_cells(0) + {} + int64_t gross_cells, net_cells; +}; + +template +struct Target { + Target() + {} + //Target(Sequence seq, Loc d_begin, Loc d_end, const Score* const* profile, Loc query_len, int64_t target_idx, bool reverse): + Target(Sequence seq, Loc d_begin, Loc d_end, Loc query_start, Loc query_len, int64_t target_idx, bool reverse) : + seq(seq), + d_begin(d_begin), + d_end(d_end), + query_start(query_start), + query_length(query_len), + target_idx(target_idx), + reverse(reverse), + score(0), + query_end(0), + target_end(0) + { + //this->profile[0] = profile; + } + Sequence seq; + Loc d_begin, d_end; + //std::array profile; + Loc query_start, query_length; + int64_t target_idx; + bool reverse; + Score score; + Loc query_end, target_end; + bool blank() const { + return seq.length() == 0; + } + void reset() { + seq = Sequence(); + } + Loc band() const { + return d_end - d_begin; + } + bool operator<(const Target& t) const { + return band() < t.band(); + } + static bool cmp_target_idx(const Target& a, const Target& b) { + return a.target_idx < b.target_idx; + } + std::pair cells() const { + int64_t n = 0, g = 0; + for (int j = 0; j < seq.length(); ++j) { + int i0 = std::max(Geo::i(j, d_begin), 0), + i1 = std::min(Geo::i(j, d_end), query_length); + //assert(i1 - i0 >= 0); + n += std::max(i1 - i0, 0); + g += d_end - d_begin; + } + return { g,n }; + } + int64_t gross_cells() const { + return int64_t(d_end - d_begin) * seq.length(); + } +}; + +}} \ No newline at end of file diff --git a/src/dp/swipe/full_matrix.h b/src/dp/swipe/full_matrix.h index 78235d23a..4799424fe 100644 --- a/src/dp/swipe/full_matrix.h +++ b/src/dp/swipe/full_matrix.h @@ -73,10 +73,10 @@ struct Matrix const int l = (int)hgap_.size(); const auto z = extract_channel(_sv(), 0); for (int i = 0; i < l; ++i) { - hgap_[i] = set_channel(hgap_[i], c, z); - score_[i] = set_channel(score_[i], c, z); + set_channel(hgap_[i], c, z); + set_channel(score_[i], c, z); } - score_[l] = set_channel(score_[l], c, z); + set_channel(score_[l], c, z); } constexpr int cols() const { return 1; @@ -146,7 +146,7 @@ struct TracebackVectorMatrix struct TracebackIterator { - TracebackIterator(const TraceMask *mask, const TraceMask* mask_begin, const TraceMask* mask_end, int rows, int i, int j, size_t channel) : + TracebackIterator(const TraceMask *mask, const TraceMask* mask_begin, const TraceMask* mask_end, int rows, int i, int j, int channel) : rows_(rows), mask_(mask), mask_begin_(mask_begin), @@ -201,7 +201,7 @@ struct TracebackVectorMatrix int i, j; }; - TracebackIterator traceback(int col, int i, int j, size_t channel) const + TracebackIterator traceback(int col, int i, int j, int channel) const { return TracebackIterator(&trace_mask_[col*rows_ + i], trace_mask_.begin(), trace_mask_.end(), rows_, i, j, channel); } @@ -226,10 +226,10 @@ struct TracebackVectorMatrix { const int l = (int)hgap_.size(); for (int i = 0; i < l; ++i) { - hgap_[i] = set_channel(hgap_[i], c, ::DISPATCH_ARCH::ScoreTraits<_sv>::zero_score()); - score_[i] = set_channel(score_[i], c, ::DISPATCH_ARCH::ScoreTraits<_sv>::zero_score()); + set_channel(hgap_[i], c, ::DISPATCH_ARCH::ScoreTraits<_sv>::zero_score()); + set_channel(score_[i], c, ::DISPATCH_ARCH::ScoreTraits<_sv>::zero_score()); } - score_[l] = set_channel(score_[l], c, ::DISPATCH_ARCH::ScoreTraits<_sv>::zero_score()); + set_channel(score_[l], c, ::DISPATCH_ARCH::ScoreTraits<_sv>::zero_score()); } int cols() const { diff --git a/src/dp/swipe/full_swipe.h b/src/dp/swipe/full_swipe.h index 4be2f2322..3cb16f3bd 100644 --- a/src/dp/swipe/full_swipe.h +++ b/src/dp/swipe/full_swipe.h @@ -41,6 +41,7 @@ Hsp traceback(_cbs bias_correction, const Matrix& dp, const DpTarget& targ out.score = ScoreTraits<_sv>::int_score(max_score) * config.cbs_matrix_scale; out.evalue = evalue; out.bit_score = score_matrix.bitscore(out.score); + out.corrected_bit_score = score_matrix.bitscore_corrected(out.score, p.query.length(), target.true_target_len); out.frame = p.frame.index(); if (target.carry_over.i1 == 0) { out.query_range.end_ = max_i + 1; @@ -53,6 +54,12 @@ Hsp traceback(_cbs bias_correction, const Matrix& dp, const DpTarget& targ out.length = target.carry_over.len; out.query_range.begin_ = (int)p.query.length() - 1 - max_i; out.subject_range.begin_ = (int)target.seq.length() - 1 - max_j; + try { + out.approx_id = out.approx_id_percent(Sequence(p.query.reverse()), Sequence(target.seq.reverse())); + } + catch (std::out_of_range&) { + throw std::runtime_error(std::string("Out_of_range query=") + std::string(p.query_id) + " target=" + target.seq.to_string()); + } } out.target_seq = target.seq; out.matrix = target.matrix; @@ -73,6 +80,7 @@ Hsp traceback(_cbs bias_correction, const TracebackVectorMatrix<_sv> &dp, const out.score = ScoreTraits<_sv>::int_score(max_score); out.evalue = evalue; out.bit_score = score_matrix.bitscore(out.score); + out.corrected_bit_score = score_matrix.bitscore_corrected(out.score, p.query.length(), target.true_target_len); out.transcript.reserve(size_t(out.score * config.transcript_len_estimate)); out.frame = p.frame.index(); @@ -109,11 +117,12 @@ Hsp traceback(_cbs bias_correction, const TracebackVectorMatrix<_sv> &dp, const out.transcript.reverse(); out.transcript.push_terminator(); out.query_source_range = TranslatedPosition::absolute_interval(TranslatedPosition(out.query_range.begin_, p.frame), TranslatedPosition(out.query_range.end_, p.frame), p.query_source_len); + out.approx_id = out.approx_id_percent(p.query, target.seq); return out; } template -list swipe(const It target_begin, const It target_end, std::atomic_size_t* const next, _cbs composition_bias, vector& overflow, Params& p) +list swipe(const It target_begin, const It target_end, std::atomic* const next, _cbs composition_bias, vector& overflow, Params& p) { typedef typename ScoreTraits<_sv>::Score Score; using Cell = typename Cfg::Cell; @@ -170,7 +179,7 @@ list swipe(const It target_begin, const It target_end, std::atomic_size_t* } #ifdef DP_STAT - stats.inc(Statistics::GROSS_DP_CELLS, uint64_t(qlen) * CHANNELS); + p.stat.inc(Statistics::GROSS_DP_CELLS, uint64_t(qlen) * CHANNELS); #endif for (int i = 0; i < qlen; ++i) { hgap = it.hgap(); @@ -211,7 +220,7 @@ list swipe(const It target_begin, const It target_end, std::atomic_size_t* else { const int s = ScoreTraits<_sv>::int_score(best[c]) * config.cbs_matrix_scale; const double evalue = score_matrix.evalue(s, qlen, (unsigned)targets.dp_targets[c].true_target_len); - if (score_matrix.report_cutoff(s, evalue)) + if (s > 0 && score_matrix.report_cutoff(s, evalue)) out.push_back(traceback<_sv>(composition_bias, dp, targets.dp_targets[c], best[c], evalue, max_col[c], max_i[c], max_j[c], c, hsp_stats[c], p)); } reinit = true; diff --git a/src/dp/swipe/stat_cell.h b/src/dp/swipe/stat_cell.h index 2feb78437..a85c2b341 100644 --- a/src/dp/swipe/stat_cell.h +++ b/src/dp/swipe/stat_cell.h @@ -96,11 +96,10 @@ struct ForwardCell { }; template -FORCE_INLINE ForwardCell set_channel(const ForwardCell& v, const int i, const typename ::DISPATCH_ARCH::ScoreTraits::Score x) { - ForwardCell c = set_channel(static_cast(v), i, x); - c.ident = set_channel(v.ident, i, x); - c.len = set_channel(v.len, i, x); - return c; +FORCE_INLINE void set_channel(ForwardCell& v, const int i, const typename ::DISPATCH_ARCH::ScoreTraits::Score x) { + set_channel((Sv&)v, i, x); + set_channel(v.ident, i, x); + set_channel(v.len, i, x); } template @@ -155,11 +154,10 @@ struct BackwardCell { }; template -FORCE_INLINE BackwardCell set_channel(const BackwardCell& v, const int i, const typename ::DISPATCH_ARCH::ScoreTraits::Score x) { - BackwardCell c = set_channel(static_cast(v), i, x); - c.mismatch = set_channel(v.mismatch, i, x); - c.gapopen = set_channel(v.gapopen, i, x); - return c; +FORCE_INLINE void set_channel(BackwardCell& v, const int i, const typename ::DISPATCH_ARCH::ScoreTraits::Score x) { + set_channel((Sv&)v, i, x); + set_channel(v.mismatch, i, x); + set_channel(v.gapopen, i, x); } struct Void { diff --git a/src/dp/swipe/swipe_wrapper.cpp b/src/dp/swipe/swipe_wrapper.cpp index 03aa6f48a..306997e8d 100644 --- a/src/dp/swipe/swipe_wrapper.cpp +++ b/src/dp/swipe/swipe_wrapper.cpp @@ -23,6 +23,7 @@ along with this program. If not, see . #include #include #include +#include #include #include "../dp.h" #include "../../util/log_stream.h" @@ -35,12 +36,16 @@ along with this program. If not, see . #include "full_matrix.h" #include "full_swipe.h" #include "banded_swipe.h" +#include "../../util/geo/geo.h" using std::list; using std::atomic; using std::thread; using std::array; using std::atomic_size_t; +using std::mutex; +using std::accumulate; +using std::string; template struct SwipeConfig { @@ -86,6 +91,8 @@ unsigned bin(HspValues v, int query_len, int score, int ungapped_score, const in else b = 2; } + else if (flag_only(v, HspValues::COORDS) && !config.approx_backtrace) + b += SCORE_BINS; } return b; } @@ -124,7 +131,7 @@ static list dispatch_swipe(const SequenceSet::ConstIterator subject_begin, } template -static list dispatch_swipe(const It begin, const It end, atomic_size_t* const next, Cbs composition_bias, vector& overflow, Params& p) +static list dispatch_swipe(const It begin, const It end, atomic* const next, Cbs composition_bias, vector& overflow, Params& p) { constexpr auto CHANNELS = vector::const_iterator::difference_type(::DISPATCH_ARCH::ScoreTraits::CHANNELS); if (flag_any(p.flags, Flags::FULL_MATRIX)) @@ -138,7 +145,7 @@ static list dispatch_swipe(const It begin, const It end, atomic_size_t* con } template -static list dispatch_swipe(const It begin, const It end, atomic_size_t* const next, vector &overflow, Params& p) +static list dispatch_swipe(const It begin, const It end, atomic* const next, vector &overflow, Params& p) { if (p.composition_bias == nullptr) return dispatch_swipe(begin, end, next, NoCBS(), overflow, p); @@ -147,7 +154,7 @@ static list dispatch_swipe(const It begin, const It end, atomic_size_t* con } template -static list dispatch_swipe(const It begin, const It end, atomic_size_t* const next, vector &overflow, const int round, const int bin, Params& p) +static list dispatch_swipe(const It begin, const It end, atomic* const next, vector &overflow, const int round, const int bin, Params& p) { if (p.v == HspValues::NONE) { using Cfg = SwipeConfig, Sv, DummyIdMask>; @@ -180,40 +187,69 @@ static list dispatch_swipe(const It begin, const It end, atomic_size_t* con throw std::runtime_error("Unreachable"); } -template -static void swipe_worker(const It begin, const It end, atomic_size_t* const next, list *out, vector *overflow, const int round, const int bin, Params* p) +template +static void swipe_worker(const It begin, const It end, atomic* const next, list *out, vector *overflow, const int round, const int bin, Params* p) { - const ptrdiff_t CHANNELS = ::DISPATCH_ARCH::ScoreTraits<_sv>::CHANNELS; + const ptrdiff_t CHANNELS = ::DISPATCH_ARCH::ScoreTraits::CHANNELS; Statistics stat2; size_t pos; vector of; Params params{ p->query, + p->query_id, p->frame, p->query_source_len, p->composition_bias, p->flags, p->v, - stat2 + stat2, + nullptr }; if (flag_any(p->flags, Flags::FULL_MATRIX)) - *out = dispatch_swipe<_sv, It>(begin, end, next, of, round, bin, params); + *out = dispatch_swipe(begin, end, next, of, round, bin, params); else while (begin + (pos = next->fetch_add(CHANNELS)) < end) { const auto start = begin + pos; - out->splice(out->end(), dispatch_swipe<_sv, It>(start, start + std::min(CHANNELS, end - start), next, of, round, bin, params)); + out->splice(out->end(), dispatch_swipe(start, start + std::min(CHANNELS, end - start), next, of, round, bin, params)); } *overflow = std::move(of); p->stat += stat2; } -template +template +static void swipe_task(const It begin, const It end, list *out, vector *overflow, mutex* mtx, const int round, const int bin, Params* p) { + const ptrdiff_t CHANNELS = ::DISPATCH_ARCH::ScoreTraits::CHANNELS; + Statistics stat2; + vector of; + atomic next(0); + Params params{ + p->query, + p->query_id, + p->frame, + p->query_source_len, + p->composition_bias, + p->flags, + p->v, + stat2, + nullptr + }; + list hsp = dispatch_swipe(begin, end, &next, of, round, bin, params); + { + std::lock_guard lock(*mtx); + overflow->insert(overflow->end(), of.begin(), of.end()); + out->splice(out->end(), hsp); + } + p->stat += stat2; +} + +template static list swipe_threads(const It begin, const It end, vector &overflow, const int round, const int bin, Params& p) { + const ptrdiff_t CHANNELS = ::DISPATCH_ARCH::ScoreTraits::CHANNELS; if (begin == end) return {}; - atomic_size_t next(0); + atomic next(0); if (flag_any(p.flags, Flags::PARALLEL)) { task_timer timer("Banded swipe (run)", config.target_parallel_verbosity); const size_t n = config.threads_align ? config.threads_align : config.threads_; @@ -221,7 +257,7 @@ static list swipe_threads(const It begin, const It end, vector &o vector> thread_out(n); vector> thread_overflow(n); for (size_t i = 0; i < n; ++i) - threads.emplace_back(swipe_worker<_sv, It>, begin, end, &next, &thread_out[i], &thread_overflow[i], round, bin, &p); + threads.emplace_back(swipe_worker, begin, end, &next, &thread_out[i], &thread_overflow[i], round, bin, &p); for (auto &t : threads) t.join(); timer.go("Banded swipe (merge)"); @@ -233,8 +269,38 @@ static list swipe_threads(const It begin, const It end, vector &o overflow.insert(overflow.end(), v.begin(), v.end()); return out; } - else - return dispatch_swipe<_sv, It>(begin, end, &next, overflow, round, bin, p); + + if(!p.thread_pool) + return dispatch_swipe(begin, end, &next, overflow, round, bin, p); + + list hsp; + ThreadPool::TaskSet task_set(*p.thread_pool, 0); + mutex mtx; + int64_t size = 0; + It i0 = begin, i1 = begin; + while(i1 < end) { + const auto n = std::min(CHANNELS, end - i1); + size += accumulate(i1, i1 + n, (int64_t)0, [&p](int64_t n, const DpTarget& t) {return n + t.cells(p.flags, p.query.length()); }); + i1 += n; + if (size >= config.swipe_task_size) { + task_set.enqueue(swipe_task, i0, i1, &hsp, &overflow, &mtx, round, bin, &p); + p.stat.inc(Statistics::SWIPE_TASKS_TOTAL); + p.stat.inc(Statistics::SWIPE_TASKS_ASYNC); + i0 = i1; + size = 0; + } + } + if (task_set.total() == 0) { + p.stat.inc(Statistics::SWIPE_TASKS_TOTAL); + return dispatch_swipe(i0, i1, &next, overflow, round, bin, p); + } + if (i1 - i0 > 0) { + p.stat.inc(Statistics::SWIPE_TASKS_TOTAL); + p.stat.inc(Statistics::SWIPE_TASKS_ASYNC); + task_set.enqueue(swipe_task, i0, i1, &hsp, &overflow, &mtx, round, bin, &p); + } + task_set.run(); + return hsp; } template @@ -296,7 +362,6 @@ static list recompute_reversed(list &hsps, Params& p) { reversed_targets.finish_reserve(); size_t j = 0; - list out; for (auto i = hsps.begin(); i != hsps.end(); ) { std::reverse_copy(i->target_seq.data(), i->target_seq.data() + i->subject_range.end_, reversed_targets.ptr(j)); const Loc band = flag_any(p.flags, Flags::FULL_MATRIX) ? qlen : i->d_end - i->d_begin, @@ -304,7 +369,8 @@ static list recompute_reversed(list &hsps, Params& p) { b = bin(p.v, band, i->score, 0, INT64_MAX, 0, mismatch_est(i->query_range.end_, tlen, i->length, p.v)); assert(b >= SCORE_BINS); const DpTarget::CarryOver carry_over{ i->query_range.end_, i->subject_range.end_, i->identities, i->length }; - dp_targets[b].emplace_back(reversed_targets[j], i->target_seq.length(), -i->d_end + qlen - tlen + 1, -i->d_begin + qlen - tlen + 1, i->swipe_target, qlen, i->matrix, carry_over); + dp_targets[b].emplace_back(reversed_targets[j], i->target_seq.length(), Geo::rev_diag(i->d_end-1, qlen, tlen), Geo::rev_diag(i->d_begin, qlen, tlen) + 1, + Interval(), 0, i->swipe_target, qlen, i->matrix, carry_over); ++i; ++j; } @@ -314,17 +380,23 @@ static list recompute_reversed(list &hsps, Params& p) { const int8_t* cbs = p.composition_bias ? rev_cbs.data() : nullptr; Params params{ Sequence(reversed), + p.query_id, p.frame, p.query_source_len, cbs, p.flags, p.v, - p.stat + p.stat, + p.thread_pool }; + list out; for (unsigned bin = SCORE_BINS; bin < BINS; ++bin) { auto r = swipe_bin(bin, dp_targets[bin].begin(), dp_targets[bin].end(), 1, params); if (!r.second.empty()) - throw std::runtime_error("Non-empty overflow list in reversed DP."); + throw std::runtime_error("Non-empty overflow list in reversed DP. Query = " + string(p.query_id) + " bin=" + std::to_string(bin) + + " target=" + r.second.front().seq.to_string() + + " d_begin=" + std::to_string(r.second.front().d_begin) + + " d_end=" + std::to_string(r.second.front().d_end)); out.splice(out.end(), r.first); } return out; diff --git a/src/dp/swipe/target_iterator.h b/src/dp/swipe/target_iterator.h index 2ef38c881..9cb5cd4c9 100644 --- a/src/dp/swipe/target_iterator.h +++ b/src/dp/swipe/target_iterator.h @@ -25,7 +25,6 @@ along with this program. If not, see . #include "../dp.h" #include "../../basic/value.h" #include "../../util/simd/vector.h" -#include "../../util/dynamic_iterator.h" #include "../../stats/hauser_correction.h" template @@ -70,7 +69,7 @@ struct TargetIterator CHANNELS = SeqVector::CHANNELS }; - TargetIterator(vector::const_iterator subject_begin, vector::const_iterator subject_end, int i1, int qlen, int *d_begin) : + TargetIterator(std::vector::const_iterator subject_begin, std::vector::const_iterator subject_end, int i1, int qlen, int *d_begin) : next(0), n_targets(int(subject_end - subject_begin)), cols(0), @@ -176,7 +175,7 @@ struct TargetIterator int pos[CHANNELS], target[CHANNELS], next, n_targets, cols; bool custom_matrix_16bit; SmallVector active; - const vector::const_iterator subject_begin; + const std::vector::const_iterator subject_begin; }; template @@ -186,13 +185,14 @@ struct AsyncTargetBuffer typedef ::DISPATCH_ARCH::SIMD::Vector SeqVector; enum { CHANNELS = SeqVector::CHANNELS }; - AsyncTargetBuffer(const It begin, const It end, std::atomic_size_t* const next): + AsyncTargetBuffer(const It begin, const It end, std::atomic* const next): begin(begin), - target_count(end - begin), + target_count(BlockId(end - begin)), next(next), custom_matrix_16bit(false) { - size_t n, i = 0; + BlockId n; + int i = 0; while (i < CHANNELS && (n = (*next)++) < target_count) { DpTarget t = begin[n]; if (t.blank()) @@ -206,7 +206,7 @@ struct AsyncTargetBuffer int max_len() const { int l = 0; - for (size_t i = 0; i < target_count; ++i) + for (BlockId i = 0; i < target_count; ++i) l = std::max(l, (int)DpTarget(begin[i]).seq.length()); return l; } @@ -258,7 +258,7 @@ struct AsyncTargetBuffer bool init_target(int i, int channel) { - const size_t n = (*next)++; + const BlockId n = (*next)++; if (n >= target_count) { active.erase(i); return false; @@ -296,8 +296,8 @@ struct AsyncTargetBuffer int pos[CHANNELS]; SmallVector active; const It begin; - const size_t target_count; - std::atomic_size_t* const next; + const BlockId target_count; + std::atomic* const next; DpTarget dp_targets[CHANNELS]; bool custom_matrix_16bit; diff --git a/src/dp/ungapped.h b/src/dp/ungapped.h index 3a3a95d9e..f1d475830 100644 --- a/src/dp/ungapped.h +++ b/src/dp/ungapped.h @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2022 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -20,14 +20,20 @@ along with this program. If not, see . #pragma once #include "../basic/value.h" -#include "../basic/diagonal_segment.h" +#include "../util/geo/diagonal_segment.h" #include "../stats/hauser_correction.h" //int xdrop_ungapped(const Letter *query, const Letter *subject, unsigned seed_len, unsigned &delta, unsigned &len); //int xdrop_ungapped(const Letter *query, const Letter *subject, unsigned &delta, unsigned &len); int xdrop_ungapped_right(const Letter *query, const Letter *subject, int &len); int ungapped_window(const Letter* query, const Letter* subject, int window); -Diagonal_segment xdrop_ungapped(const Sequence &query, const Bias_correction &query_bc, const Sequence &subject, int qa, int sa); -Diagonal_segment xdrop_ungapped(const Sequence &query, const Sequence &subject, int qa, int sa); +DiagonalSegment xdrop_ungapped(const Sequence &query, const Bias_correction &query_bc, const Sequence &subject, int qa, int sa); +DiagonalSegment xdrop_ungapped(const Sequence& query, const int8_t* query_cbs, const Sequence& subject, int qa, int sa, bool count_identities); +DiagonalSegment xdrop_ungapped(const Sequence& query, const Sequence& subject, const DiagonalSegment& anchor); int score_range(Sequence query, Sequence subject, int i, int j, int j_end); -int self_score(const Sequence& seq); \ No newline at end of file +template +DiagonalSegment score_range_s(Sequence query, Cbs query_cbs, Sequence subject, int i, int j, int j_end); +Score self_score(const Sequence& seq); +Hsp trivial(Sequence query, Sequence target, const int8_t* query_cbs); +Anchor make_clipped_anchor(const Anchor& anchor, Sequence query, const int8_t* query_cbs, Sequence target); +Anchor make_null_anchor(const Anchor& anchor); \ No newline at end of file diff --git a/src/dp/ungapped_align.cpp b/src/dp/ungapped_align.cpp index 2116fd5e3..0e9ce97f0 100644 --- a/src/dp/ungapped_align.cpp +++ b/src/dp/ungapped_align.cpp @@ -16,65 +16,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ +#include #include "dp.h" #include "../stats/score_matrix.h" #include "../stats/hauser_correction.h" +#include "ungapped.h" +#include "../util/sequence/sequence.h" -/*int xdrop_ungapped(const Letter *query, const Letter *subject, unsigned seed_len, unsigned &delta, unsigned &len) -{ - int score(0), st(0); - unsigned n(0); - delta = 0; - - const Letter *q(query - 1), *s(subject - 1); - const unsigned window_left = std::max(config.window, (unsigned)config.seed_anchor) - config.seed_anchor; - while (score - st < config.raw_ungapped_xdrop - && delta < window_left - && *q != sequence::DELIMITER - && *s != sequence::DELIMITER) - { -#ifdef SEQ_MASK - st += score_matrix(letter_mask(*q), letter_mask(*s)); -#else - st += score_matrix(*q, *s); -#endif - score = std::max(score, st); - --q; - --s; - ++delta; - } - - q = query + seed_len; - s = subject + seed_len; - st = score; - assert(seed_len >= config.seed_anchor); - const unsigned window_right = std::max(config.window, seed_len - config.seed_anchor) - (seed_len - config.seed_anchor); - while (score - st < config.raw_ungapped_xdrop - && n < window_right - && *q != sequence::DELIMITER - && *s != sequence::DELIMITER) - { -#ifdef SEQ_MASK - st += score_matrix(letter_mask(*q), letter_mask(*s)); -#else - st += score_matrix(*q, *s); -#endif - score = std::max(score, st); - ++q; - ++s; - ++n; - } - - for (unsigned i = 0; i +DiagonalSegment xdrop_ungapped(const Sequence& query, Cbs query_cbs, const Sequence& subject, int qa, int sa, const Id&) { + Id id; const int xdrop = config.raw_ungapped_xdrop; int score = 0, st = 0; - int n = 1, delta = 0, len = 0; + int n = 1, delta = 0, len = 0, ident = 0; int q = qa - 1, s = sa - 1; Letter ql, sl; @@ -181,9 +162,12 @@ Diagonal_segment xdrop_ungapped(const Sequence& query, const Sequence& subject, && (sl = subject[s]) != Sequence::DELIMITER) { st += score_matrix(ql, sl); + add_cbs(st, q, query_cbs); + id(ql, sl); if (st > score) { score = st; delta = n; + id.update(ident); } --q; --s; @@ -194,20 +178,39 @@ Diagonal_segment xdrop_ungapped(const Sequence& query, const Sequence& subject, s = sa; st = score; n = 1; + id = Id(); while (score - st < xdrop && (ql = query[q]) != Sequence::DELIMITER && (sl = subject[s]) != Sequence::DELIMITER) { st += score_matrix(ql, sl); + add_cbs(st, q, query_cbs); + id(ql, sl); if (st > score) { score = st; len = n; + id.update(ident); } ++q; ++s; ++n; } - return Diagonal_segment(qa - delta, sa - delta, len + delta, score); + return DiagonalSegment(qa - delta, sa - delta, len + delta, score, ident); +} + +DiagonalSegment xdrop_ungapped(const Sequence& query, const int8_t* query_cbs, const Sequence& subject, int qa, int sa, bool count_identities) { + if (count_identities) { + if (query_cbs == nullptr) + return xdrop_ungapped(query, nullptr, subject, qa, sa, CountIdentities()); + else + return xdrop_ungapped(query, query_cbs, subject, qa, sa, CountIdentities()); + } + else { + if (query_cbs == nullptr) + return xdrop_ungapped(query, nullptr, subject, qa, sa, ScoreOnly()); + else + return xdrop_ungapped(query, query_cbs, subject, qa, sa, ScoreOnly()); + } } int xdrop_ungapped_right(const Letter *query, const Letter *subject, int &len) @@ -243,11 +246,7 @@ int ungapped_window(const Letter* query, const Letter* subject, int window) { const Letter* q = query, * s = subject; while (n < window) { -#ifdef SEQ_MASK - st += score_matrix(letter_mask(*q), *s); -#else - st += score_matrix(*q, *s); -#endif + st += score_matrix(letter_mask(*q), letter_mask(*s)); st = std::max(st, 0); score = std::max(score, st); ++q; @@ -257,21 +256,174 @@ int ungapped_window(const Letter* query, const Letter* subject, int window) { return score; } -int self_score(const Sequence& seq) +Score self_score(const Sequence& seq) { - int s = 0; + Score s = 0, sl = 0; if (Stats::CBS::hauser(config.comp_based_stats)) { Bias_correction cbs(seq); + for (Loc i = 0; i < seq.length(); ++i) { const Letter l = seq[i]; - s += score_matrix(l, l) + cbs.int8[i]; + sl += score_matrix(l, l) + cbs.int8[i]; + sl = max(sl, 0); + s = max(s, sl); } } else { for (Loc i = 0; i < seq.length(); ++i) { const Letter l = seq[i]; - s += score_matrix(l, l); + sl += score_matrix(l, l); + sl = max(sl, 0); + s = max(s, sl); } } return s; +} + +int score_range(Sequence query, Sequence subject, int i, int j, int j_end) +{ + int score = 0; + while (j < j_end) { + score += score_matrix(query[i], subject[j]); + ++i; + ++j; + } + return score; +} + +template +DiagonalSegment score_range_s(Sequence query, Cbs query_cbs, Sequence subject, int i_begin, int j_begin, int j_end) { + int score = 0, i = i_begin; + for (Loc j = j_begin; j < j_end; ++j, ++i) { + score += score_matrix(query[i], subject[j]); + add_cbs(score, i, query_cbs); + } + return DiagonalSegment(i_begin, j_begin, j_end - j_begin, score); +} + +template DiagonalSegment score_range_s(Sequence, const int8_t*, Sequence, int, int, int); +template DiagonalSegment score_range_s(Sequence, nullptr_t, Sequence, int, int, int); + +template +struct Increment { + T operator()(T x) const { + return ++x; + } +}; + +template +struct Decrement { + T operator()(T x) const { + return --x; + } +}; + +template +pair xdrop_anchored(const Letter* p1, const Letter* p2, Loc len, Inc inc) { + if (len == 0) + return { 0,0 }; + Score max_score = 0, score = 0; + Loc max_n = 0, n = 0; + do { + score += score_matrix(letter_mask(*p1), letter_mask(*p2)); + ++n; + p1 = inc(p1); + p2 = inc(p2); + if (score > max_score) { + max_score = score; + max_n = n; + } + } while (n < len && max_score - score < config.raw_ungapped_xdrop); + return { max_score, max_n }; +} + +DiagonalSegment xdrop_ungapped(const Sequence& query, const Sequence& subject, const DiagonalSegment& anchor) { + auto left = xdrop_anchored(query.data() + anchor.i - 1, subject.data() + anchor.j - 1, std::min(anchor.i, anchor.j), Decrement()); + auto right = xdrop_anchored(query.data() + anchor.query_end(), + subject.data() + anchor.subject_end(), + std::min(query.length() - anchor.query_end(), subject.length() - anchor.subject_end()), + Increment()); + return { anchor.i - left.second, anchor.j - left.second, + anchor.len + left.second + right.second, + left.first + right.first + score_range(query, subject, anchor.i, anchor.j, anchor.subject_end()) }; +} + +static Hsp trivial(Sequence query, Sequence target, Loc dq, Loc dt, const int8_t* query_cbs) { + static const Loc WINDOW = 40, ID = 30; + const Loc l = min(query.length() - dq, target.length() - dt); + const uint64_t bits = ((uint64_t)1 << WINDOW) - 1; + Loc n = 0; + Score score = 0; + uint64_t mask = 0; + for (Loc i = 0; i < l; ++i) { + uint64_t eq = query[i + dq] == target[i + dt]; + mask = ((mask << 1) | eq) & bits; + ++n; + if (n >= WINDOW && popcount64(mask) < ID) + return Hsp(); + score += score_matrix(query[i + dq], target[i + dt]); + if (query_cbs) + score += query_cbs[i + dq]; + } + const double evalue = score_matrix.evalue(score, query.length(), target.length()); + if (evalue > config.max_evalue) + return Hsp(); + if (l < WINDOW && (double)popcount64(mask) / l < ID / WINDOW) + return Hsp(); + Hsp hsp; + hsp.score = score; + hsp.query_range = hsp.query_source_range = { dq, dq + l }; + hsp.subject_range = { dt, dt + l }; + hsp.evalue = evalue; + hsp.bit_score = score_matrix.bitscore(score); + return hsp; +} + +Hsp trivial(Sequence query, Sequence target, const int8_t* query_cbs) { + if (query.length() <= target.length()) { + for (Loc i = 0; i <= target.length() - query.length(); ++i) { + Hsp hsp = trivial(query, target, 0, i, query_cbs); + if (hsp.score) + return hsp; + } + } + else { + for (Loc i = 0; i <= query.length() - target.length(); ++i) { + Hsp hsp = trivial(query, target, i, 0, query_cbs); + if (hsp.score) + return hsp; + } + } + return Hsp(); +} + +Anchor make_clipped_anchor(const Anchor& anchor, Sequence query, const int8_t* query_cbs, Sequence target) { + const Sequence q = query.subseq(anchor.query_begin(), anchor.query_end()), + t = target.subseq(anchor.subject_begin(), anchor.subject_end()); + const vector s = Util::Seq::window_scores(q, t, config.anchor_window); + const Score cutoff = (Score)round(config.anchor_score * config.anchor_window); + const auto pred = [cutoff](Score s) { return s >= cutoff; }; + + const auto max_window = max_element(s.begin(), s.end()); + const auto right = find_if_not(max_window + 1, s.cend(), pred); + + const auto max_window_r = s.crbegin() + (s.cend() - max_window - 1); + const auto left_r = find_if_not(max_window_r + 1, s.crend(), pred); + Loc d1 = Loc(right - s.cbegin()), + d0 = std::max(Loc(s.crend() - left_r - config.anchor_window + 1), 0); + while (d0 < q.length() && q[d0] != t[d0]) ++d0; + while (d1 > 0 && q[d1 - 1] != t[d1 - 1]) --d1; + if (d1 <= d0) + return DiagonalSegment(); + const DiagonalSegment clipped_anchor = query_cbs == nullptr + ? score_range_s(query, nullptr, target, anchor.query_begin() + d0, anchor.subject_begin() + d0, anchor.subject_begin() + d1) + : score_range_s(query, query_cbs, target, anchor.query_begin() + d0, anchor.subject_begin() + d0, anchor.subject_begin() + d1); + const Score clipped_score = query_cbs == nullptr + ? score_range_s(query, nullptr, target, anchor.query_begin() + d1, anchor.subject_begin() + d1, anchor.subject_end()).score + : score_range_s(query, query_cbs, target, anchor.query_begin() + d1, anchor.subject_begin() + d1, anchor.subject_end()).score; + return Anchor(clipped_anchor, anchor.d_min_left, anchor.d_max_left, anchor.d_min_right, anchor.d_max_right, anchor.prefix_score - clipped_score); +} + +Anchor make_null_anchor(const Anchor& anchor) { + return Anchor(anchor.i + anchor.len / 2, anchor.j + anchor.len / 2, 0, 0); } \ No newline at end of file diff --git a/src/util/task_queue.h b/src/legacy/util/task_queue.h similarity index 94% rename from src/util/task_queue.h rename to src/legacy/util/task_queue.h index 602f52126..545f3e998 100644 --- a/src/util/task_queue.h +++ b/src/legacy/util/task_queue.h @@ -27,11 +27,11 @@ along with this program. If not, see . // #define ENABLE_LOGGING -template -struct Task_queue +template +struct TaskQueue { - Task_queue(size_t limit, _callback &callback): + TaskQueue(size_t limit, Callback& callback): queue_ (limit), state_ (limit), head_ (0), @@ -48,7 +48,7 @@ struct Task_queue { return tail_ - head_ >= limit_; } template - bool get(size_t &n, _t*& res, _init &init) + bool get(size_t &n, T*& res, _init &init) { { std::unique_lock lock(mtx_); @@ -136,15 +136,15 @@ struct Task_queue size_t idx(size_t n) const { return (head_idx_ + n - head_)%limit_; } - _t& slot(size_t n) + T& slot(size_t n) { return queue_[idx(n)]; } - std::vector<_t> queue_; + std::vector queue_; std::vector state_; std::mutex mtx_; std::condition_variable cond_; volatile size_t head_, tail_, limit_, head_idx_, queued_, queued_size_; bool at_end_; - _callback &callback_; + Callback &callback_; }; diff --git a/src/lib/alp/njn_dynprogprob.cpp b/src/lib/alp/njn_dynprogprob.cpp index dc88f1e65..beb63cbb6 100644 --- a/src/lib/alp/njn_dynprogprob.cpp +++ b/src/lib/alp/njn_dynprogprob.cpp @@ -225,7 +225,7 @@ void DynProgProb::update () // updates dynamic prog probs value = getValueFct () (i, j); while (value < getValueBegin () || getValueEnd () <= value) { valueBegin = getValueBegin (); - if (value < getValueBegin ()) valueBegin -= (ARRAY_FAC - 1) * getArrayCapacity (); + if (value < getValueBegin ()) valueBegin -= (ARRAY_FAC - 1) * (long)getArrayCapacity (); reserve (ARRAY_FAC * getArrayCapacity ()); setValueBegin (valueBegin); oldArray = d_array_p [d_step % 2]; diff --git a/src/lib/alp/sls_alignment_evaluer.cpp b/src/lib/alp/sls_alignment_evaluer.cpp index 05e534232..c9d270025 100644 --- a/src/lib/alp/sls_alignment_evaluer.cpp +++ b/src/lib/alp/sls_alignment_evaluer.cpp @@ -1028,6 +1028,31 @@ double seqlen2_) const//length of sequence #2 } +double AlignmentEvaluer::log_area(double score_,//pairwise alignment score + double seqlen1_,//length of sequence #1 + double seqlen2_) const//length of sequence #2 +{ + if (seqlen1_ <= 0 || seqlen2_ <= 0) + { + throw error("Error - seqlen1_<=0 or seq2en1_<=0 in \"double AlignmentEvaluer::area\"\n", 2); + }; + + if (!isGood()) + { + throw error("Unexpected error - the Gumbel parameters are not defined properly in \"double AlignmentEvaluer::area\"\n", 1); + }; + + static Sls::pvalues pvalues_obj; + + + return pvalues_obj.log_area( + d_params, + pvalues_obj.blast, + score_, + seqlen2_, + seqlen1_); +} + void AlignmentEvaluer::calc(double score_,//pairwise alignment score double seqlen1_,//length of sequence #1 double seqlen2_,//length of sequence #2 diff --git a/src/lib/alp/sls_alignment_evaluer.hpp b/src/lib/alp/sls_alignment_evaluer.hpp index 143b583e5..0dc9ef221 100644 --- a/src/lib/alp/sls_alignment_evaluer.hpp +++ b/src/lib/alp/sls_alignment_evaluer.hpp @@ -151,6 +151,10 @@ namespace Sls { double seqlen1_,//length of sequence #1 double seqlen2_) const;//length of sequence #2 + double log_area(double score_,//pairwise alignment score + double seqlen1_,//length of sequence #1 + double seqlen2_) const; //length of sequence #2 + double evaluePerArea(double score_) const { return d_params.K*exp(-d_params.lambda*score_); diff --git a/src/lib/alp/sls_pvalues.cpp b/src/lib/alp/sls_pvalues.cpp index e7ab1a2af..4e009c484 100644 --- a/src/lib/alp/sls_pvalues.cpp +++ b/src/lib/alp/sls_pvalues.cpp @@ -38,6 +38,7 @@ Contents: Calculation of P-values using precalculated Gumbel parameters #include // std::setprecision #include "sls_normal_distr_array.hpp" +#include "../gsl/gsl.h" using namespace Sls; @@ -544,6 +545,164 @@ bool compute_only_area_) } +static double log_sum(double a, double b) { + if (a < b) + std::swap(a, b); + return a + log(1 + exp(b - a)); +} + +static double log_diff(double a, double b) { + if (a < b) + throw std::runtime_error("Taking log of negative number."); + return a + log(1 - exp(b - a)); +} + +double pvalues::log_area( + const ALP_set_of_parameters &par_, + bool blast_, + double y_, + double m_, + double n_) +{ + + //to optimize performance + blast_ = false; + + double lambda_ = par_.lambda; + double k_ = par_.K; + + double ai_hat_ = par_.a_I; + double bi_hat_; + double alphai_hat_ = par_.alpha_I; + double betai_hat_; + + double aj_hat_ = par_.a_J; + double bj_hat_; + double alphaj_hat_ = par_.alpha_J; + double betaj_hat_; + + double sigma_hat_ = par_.sigma; + double tau_hat_; + + { + bi_hat_ = par_.b_I; + betai_hat_ = par_.beta_I; + + bj_hat_ = par_.b_J; + betaj_hat_ = par_.beta_J; + + tau_hat_ = par_.tau; + }; + + if (blast_) + { + alphai_hat_ = 0; + betai_hat_ = 0; + + alphaj_hat_ = 0; + betaj_hat_ = 0; + + sigma_hat_ = 0; + tau_hat_ = 0; + }; + + double m_li_y = 0; + + double tmp = ai_hat_ * y_ + bi_hat_; + + m_li_y = m_ - tmp; + + double vi_y = 0; + + vi_y = alp_data::Tmax(par_.vi_y_thr, alphai_hat_*y_ + betai_hat_); + + double sqrt_vi_y = sqrt(vi_y); + + + double m_F; + + if (sqrt_vi_y == 0.0 || blast_) + { + m_F = 1e100; + } + else + { + m_F = m_li_y / sqrt_vi_y; + }; + + + double log_P_m_F = log(0.5) + gsl_sf_log_erfc(-sqrt(0.5) * m_F); + + double log_minus_E_m_F = log(const_val) + (-0.5*m_F*m_F); + double log_minus_sqrt_vi_y_E_m_F = log(sqrt_vi_y) + log_minus_E_m_F; + + double log_p1; + if (m_li_y < 0) { + double log_minus_m_li_y_P_m_F = log(-m_li_y) + log_P_m_F; + log_p1 = log_diff(log_minus_sqrt_vi_y_E_m_F, log_minus_m_li_y_P_m_F); + } + else { + double log_m_li_y_P_m_F = log(m_li_y) + log_P_m_F; + log_p1 = log_sum(log_minus_sqrt_vi_y_E_m_F, log_m_li_y_P_m_F); + } + + double n_lj_y = 0; + + tmp = aj_hat_ * y_ + bj_hat_; + + n_lj_y = n_ - tmp; + + double vj_y = 0; + + vj_y = alp_data::Tmax(par_.vj_y_thr, alphaj_hat_*y_ + betaj_hat_); + + double sqrt_vj_y = sqrt(vj_y); + + double n_F; + + if (sqrt_vj_y == 0.0 || blast_) + { + n_F = 1e100; + } + else + { + n_F = n_lj_y / sqrt_vj_y; + }; + + double log_P_n_F = log(0.5) + gsl_sf_log_erfc(-sqrt(0.5) * n_F); + + double log_minus_E_n_F = log(const_val) + (-0.5*n_F*n_F); + double log_minus_sqrt_vj_y_E_n_F = log(sqrt_vj_y) + log_minus_E_n_F; + double log_p2; + + if (n_lj_y < 0) { + double log_minus_n_lj_y_P_n_F = log(-n_lj_y) + log_P_n_F; + log_p2 = log_diff(log_minus_sqrt_vj_y_E_n_F, log_minus_n_lj_y_P_n_F); + } + else { + double log_n_lj_y_P_n_F = log(n_lj_y) + log_P_n_F; + log_p2 = log_sum(log_minus_sqrt_vj_y_E_n_F, log_n_lj_y_P_n_F); + } + + double log_c_y = 0; + + log_c_y = log(alp_data::Tmax(par_.c_y_thr, sigma_hat_*y_ + tau_hat_)); + + double log_P_m_F_P_n_F = log_P_m_F + log_P_n_F; + + double log_c_y_P_m_F_P_n_F = log_c_y + log_P_m_F_P_n_F; + + double log_p1_p2 = log_p1 + log_p2; + + double log_area = log_sum(log_p1_p2, log_c_y_P_m_F_P_n_F); + + if (!isfinite(log_area)) + throw std::runtime_error("Numerical error in area computation."); + + return log_area; +} + + void pvalues::get_P_error_using_splitting_method( const ALP_set_of_parameters &par_, bool blast_, @@ -559,7 +718,7 @@ double &E_error_, bool &area_is_1_flag_) { - long int dim=par_.m_LambdaSbs.size(); + long int dim=(long)par_.m_LambdaSbs.size(); if(dim==0) { throw error("Unexpected error in get_P_error_using_splitting_method\n",1); @@ -579,7 +738,6 @@ bool &area_is_1_flag_) vector E_values(dim); vector exp_E_values(dim); - long int i; for(i=0;i #include +#include + /****************************** Constants *********************************/ @@ -103,9 +105,8 @@ extern const int kUngappedHSPNumMax; #endif /** Safe free a pointer: belongs to a higher level header. */ -#ifndef sfree #define sfree(x) __sfree((void**)(void*)&(x)) -#endif + /** Implemented in blast_util.c. @sa sfree */ inline void diff --git a/src/lib/blast/blast_encoding.h b/src/lib/blast/blast_encoding.h index 07a040c54..210d624a8 100644 --- a/src/lib/blast/blast_encoding.h +++ b/src/lib/blast/blast_encoding.h @@ -66,19 +66,19 @@ typedef enum { /** Translates between ncbi4na and blastna. The first four elements * of this array match ncbi2na. */ -NCBI_XBLAST_EXPORT extern const Uint1 NCBI4NA_TO_BLASTNA[]; +NCBI_XBLAST_EXPORT extern const int NCBI4NA_TO_BLASTNA[]; /** Translates between blastna and ncbi4na. */ -NCBI_XBLAST_EXPORT extern const Uint1 BLASTNA_TO_NCBI4NA[]; +NCBI_XBLAST_EXPORT extern const int BLASTNA_TO_NCBI4NA[]; /** Translates between iupacna and blastna. */ -NCBI_XBLAST_EXPORT extern const Uint1 IUPACNA_TO_BLASTNA[]; +NCBI_XBLAST_EXPORT extern const int IUPACNA_TO_BLASTNA[]; /** Translates between iupacna and ncbi4na. */ -NCBI_XBLAST_EXPORT extern const Uint1 IUPACNA_TO_NCBI4NA[]; +NCBI_XBLAST_EXPORT extern const int IUPACNA_TO_NCBI4NA[]; /** Translates between ncbieaa and ncbistdaa. */ -NCBI_XBLAST_EXPORT extern const Uint1 AMINOACID_TO_NCBISTDAA[]; +NCBI_XBLAST_EXPORT extern const int AMINOACID_TO_NCBISTDAA[]; /** Translates between ncbieaa and ncbistdaa. */ NCBI_XBLAST_EXPORT extern const char NCBISTDAA_TO_AMINOACID[]; @@ -100,9 +100,9 @@ NCBI_XBLAST_EXPORT extern const char NCBI4NA_TO_IUPACNA[]; #define NCBI4NA_SEQ_CODE 4 /**< == Seq_code_ncbi4na */ /** Sentinel byte for protein sequences */ -NCBI_XBLAST_EXPORT extern const Uint1 kProtSentinel; +NCBI_XBLAST_EXPORT extern const int kProtSentinel; /** Sentinel nibble for nucleotide sequences */ -NCBI_XBLAST_EXPORT extern const Uint1 kNuclSentinel; +NCBI_XBLAST_EXPORT extern const int kNuclSentinel; #ifdef __cplusplus } diff --git a/src/lib/blast/blast_export.h b/src/lib/blast/blast_export.h new file mode 100644 index 000000000..1554fb6db --- /dev/null +++ b/src/lib/blast/blast_export.h @@ -0,0 +1,70 @@ +#ifndef BLAST_EXPORT__H +#define BLAST_EXPORT__H + +/* $Id$ + * =========================================================================== + * + * PUBLIC DOMAIN NOTICE + * National Center for Biotechnology Information + * + * This software/database is a "United States Government Work" under the + * terms of the United States Copyright Act. It was written as part of + * the author's official duties as a United States Government employee and + * thus cannot be copyrighted. This software/database is freely available + * to the public for use. The National Library of Medicine and the U.S. + * Government have not placed any restriction on its use or reproduction. + * + * Although all reasonable efforts have been taken to ensure the accuracy + * and reliability of the software and data, the NLM and the U.S. + * Government do not and cannot warrant the performance or results that + * may be obtained by using this software or data. The NLM and the U.S. + * Government disclaim all warranties, express or implied, including + * warranties of performance, merchantability or fitness for any particular + * purpose. + * + * Please cite the author in any work or product based on this material. + * + * =========================================================================== + * + * Author: Viatcheslav Gorelenkov + * + */ + +/** @file blast_export.h + * Defines to provide correct exporting from BLAST DLL in Windows. + * These are necessary to compile DLLs with Visual C++ - exports must be + * explicitly labeled as such. + */ + + + + +#if defined(WIN32) && defined(NCBI_DLL_BUILD) + +#ifndef _MSC_VER +# error "This toolkit is not buildable with a compiler other than MSVC." +#endif + + +#ifdef NCBI_XALGO_EXPORTS +# define NCBI_XBLAST_EXPORT __declspec(dllexport) +#else +# define NCBI_XBLAST_EXPORT __declspec(dllimport) +#endif + +#elif defined(HAVE_ATTRIBUTE_VISIBILITY_DEFAULT) + +# define NCBI_XBLAST_EXPORT __attribute__((visibility("default"))) + +#else + +/** + * NULL operations for other cases + */ + +# define NCBI_XBLAST_EXPORT + + +#endif + +#endif /* BLAST_EXPORT__H */ diff --git a/src/lib/blast/blast_message.cpp b/src/lib/blast/blast_message.cpp new file mode 100644 index 000000000..2d690c74d --- /dev/null +++ b/src/lib/blast/blast_message.cpp @@ -0,0 +1,145 @@ +/* $Id$ + * =========================================================================== + * + * PUBLIC DOMAIN NOTICE + * National Center for Biotechnology Information + * + * This software/database is a "United States Government Work" under the + * terms of the United States Copyright Act. It was written as part of + * the author's official duties as a United States Government employee and + * thus cannot be copyrighted. This software/database is freely available + * to the public for use. The National Library of Medicine and the U.S. + * Government have not placed any restriction on its use or reproduction. + * + * Although all reasonable efforts have been taken to ensure the accuracy + * and reliability of the software and data, the NLM and the U.S. + * Government do not and cannot warrant the performance or results that + * may be obtained by using this software or data. The NLM and the U.S. + * Government disclaim all warranties, express or implied, including + * warranties of performance, merchantability or fitness for any particular + * purpose. + * + * Please cite the author in any work or product based on this material. + * + * =========================================================================== + */ + +/** @file blast_message.c + * These functions provide access to Blast_Message objects, used by + * the BLAST code as a wrapper for error and warning messages. + */ + +#include "blast_def.h" +#include "blast_message.h" +#include + +/** Declared in blast_message.h as extern const. */ +const int kBlastMessageNoContext = -1; +const char* kBlastErrMsg_CantCalculateUngappedKAParams + = "Could not calculate ungapped Karlin-Altschul parameters due " + "to an invalid query sequence or its translation. Please verify the " + "query sequence(s) and/or filtering options"; + +/** Allocate a new SMessageOrigin structure + * @param filename name of the file [in] + * @param lineno line number in the file above [in] + * @return newly allocated structure or NULL in case of memory allocation + * failure. + */ +SMessageOrigin* SMessageOriginNew(const char* filename, unsigned int lineno) +{ + SMessageOrigin* retval = NULL; + + if ( !filename || !(strlen(filename) > 0) ) { + return NULL; + } + + if ( !retval ) { + return NULL; + } + + retval->filename = strdup(filename); + retval->lineno = lineno; + return retval; +} + +/** Deallocate a SMessageOrigin structure + * @param msgo structure to deallocate [in] + * @return NULL + */ +SMessageOrigin* SMessageOriginFree(SMessageOrigin* msgo) +{ + if (msgo) { + sfree(msgo->filename); + sfree(msgo); + } + return 0; +} + +Blast_Message* +Blast_MessageFree(Blast_Message* blast_msg) +{ + Blast_Message* var_msg = 0; + Blast_Message* next = 0; + + if (blast_msg == 0) + return 0; + + var_msg = blast_msg; + while (var_msg) + { + sfree(var_msg->message); + var_msg->origin = SMessageOriginFree(var_msg->origin); + next = var_msg->next; + sfree(var_msg); + var_msg = next; + } + + return 0; +} + +int +Blast_MessageWrite(Blast_Message* *blast_msg, EBlastSeverity severity, + int context, const char *message) +{ + Blast_Message* new_msg = 0; + + if (blast_msg == 0) + return 1; + + new_msg = (Blast_Message*) calloc(1, sizeof(Blast_Message)); + if (new_msg == 0) + return -1; + + new_msg->severity = severity; + new_msg->context = context; + new_msg->message = strdup(message); + + if (*blast_msg) + { + Blast_Message* var_msg = *blast_msg; + while (var_msg->next) + { + var_msg = var_msg->next; + } + var_msg->next = new_msg; + } + else + { + *blast_msg = new_msg; + } + + return 0; +} + +int +Blast_MessagePost(Blast_Message* blast_msg) +{ + if (blast_msg == 0) + return 1; + + + return 0; +} + + diff --git a/src/lib/blast/blast_message.h b/src/lib/blast/blast_message.h index 0534868cf..fed6bbdd2 100644 --- a/src/lib/blast/blast_message.h +++ b/src/lib/blast/blast_message.h @@ -89,7 +89,7 @@ Blast_Message* Blast_MessageFree(Blast_Message* blast_msg); */ NCBI_XBLAST_EXPORT -Int2 Blast_MessageWrite(Blast_Message* *blast_msg, EBlastSeverity severity, +int Blast_MessageWrite(Blast_Message* *blast_msg, EBlastSeverity severity, int context, const char *message); @@ -98,7 +98,7 @@ Int2 Blast_MessageWrite(Blast_Message* *blast_msg, EBlastSeverity severity, */ NCBI_XBLAST_EXPORT -Int2 Blast_MessagePost(Blast_Message* blast_msg); +int Blast_MessagePost(Blast_Message* blast_msg); /* FIXME: should the code below and its implementation be moved to another * file, say blast_error.[hc]? */ @@ -110,7 +110,7 @@ Int2 Blast_MessagePost(Blast_Message* blast_msg); * @return Blast_Message structure containing error description */ NCBI_XBLAST_EXPORT -void Blast_Perror(Blast_Message* *msg, Int2 error_code, int context); +void Blast_Perror(Blast_Message* *msg, int error_code, int context); /** Convenient define to call the function Blast_PerrorEx. */ #define Blast_PerrorWithLocation(msg, error_code, context) \ @@ -127,7 +127,7 @@ Blast_PerrorEx(msg, error_code, __FILE__, __LINE__, context) */ NCBI_XBLAST_EXPORT void Blast_PerrorEx(Blast_Message* *msg, - Int2 error_code, + int error_code, const char* file_name, int lineno, int context); diff --git a/src/lib/blast/blast_options.h b/src/lib/blast/blast_options.h new file mode 100644 index 000000000..ede56fd29 --- /dev/null +++ b/src/lib/blast/blast_options.h @@ -0,0 +1,1215 @@ +/* $Id$ + * =========================================================================== + * + * PUBLIC DOMAIN NOTICE + * National Center for Biotechnology Information + * + * This software/database is a "United States Government Work" under the + * terms of the United States Copyright Act. It was written as part of + * the author's official duties as a United States Government employee and + * thus cannot be copyrighted. This software/database is freely available + * to the public for use. The National Library of Medicine and the U.S. + * Government have not placed any restriction on its use or reproduction. + * + * Although all reasonable efforts have been taken to ensure the accuracy + * and reliability of the software and data, the NLM and the U.S. + * Government do not and cannot warrant the performance or results that + * may be obtained by using this software or data. The NLM and the U.S. + * Government disclaim all warranties, express or implied, including + * warranties of performance, merchantability or fitness for any particular + * purpose. + * + * Please cite the author in any work or product based on this material. + * + * =========================================================================== + * + * Author: Tom Madden + * + */ + +/** @file blast_options.h + * The structures and functions in blast_options.[ch] should be used to specify + * user preferences. The options structures should not be changed by the BLAST code + * but rather be read to determine user preferences. When possible these structures + * should be passed in as "const". + */ + +#ifndef __BLASTOPTIONS__ +#define __BLASTOPTIONS__ + +#include "ncbi_std.h" +#include "blast_export.h" +#include "blast_program.h" +#include "blast_def.h" +#include "blast_message.h" +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** Some default values (used when creating blast options block and for + * command-line program defaults. When changing these defaults, please + * remember to update the defaults in the command-line programs + */ + +/** "window" between hits to trigger an extension. */ +#define BLAST_WINDOW_SIZE_PROT 40 /**< default window (all protein searches) */ +#define BLAST_WINDOW_SIZE_NUCL 0 /**< default window size (blastn) */ +#define BLAST_WINDOW_SIZE_MEGABLAST 0 /**< default window size + (contiguous megablast) */ +#define BLAST_WINDOW_SIZE_DISC 40 /**< default window size + (discontiguous megablast) */ +#define BLAST_SCAN_RANGE_NUCL 0 /**< default scan range (blastn) */ + +/** length of word to trigger an extension. */ +#define BLAST_WORDSIZE_PROT 3 /**< default word size (all protein searches) */ +#define BLAST_WORDSIZE_NUCL 11 /**< default word size (blastn) */ +#define BLAST_WORDSIZE_MEGABLAST 28 /**< default word size (contiguous + megablast; for discontig megablast + the word size is explicitly + overridden) */ + +#define BLAST_WORDSIZE_MAPPER 18 /**< default word size for mapping rna-seq + to a genome */ + +/** Default matrix name: BLOSUM62 */ +#define BLAST_DEFAULT_MATRIX "BLOSUM62" + +/** Protein gap costs are the defaults for the BLOSUM62 scoring matrix. + * More gap costs are listed in BLASTOptionSetGapParams + */ + +/** cost for the existence of a gap.*/ +#define BLAST_GAP_OPEN_PROT 11 /**< default gap open penalty (all + protein searches) */ +#define BLAST_GAP_OPEN_NUCL 5 /**< default gap open penalty (blastn) */ +#define BLAST_GAP_OPEN_MEGABLAST 0 /**< default gap open penalty (megablast + with greedy gapped alignment) */ +#define BLAST_GAP_OPEN_MAPPER 0 + +/** cost to extend a gap. */ +#define BLAST_GAP_EXTN_PROT 1 /**< default gap open penalty (all + protein searches) */ +#define BLAST_GAP_EXTN_NUCL 2 /**< default gap open penalty (blastn) */ +#define BLAST_GAP_EXTN_MEGABLAST 0 /**< default gap open penalty (megablast) + with greedy gapped alignment) */ + +#define BLAST_GAP_EXTN_MAPPER 4 + +/** neighboring word score thresholds; a threshold of zero + * means that only query and subject words that match exactly + * will go into the BLAST lookup table when it is generated + */ +#define BLAST_WORD_THRESHOLD_BLASTP 11 /**< default neighboring threshold + (blastp and for rpsblast at RPS-BLAST + database creation time) */ +#define BLAST_WORD_THRESHOLD_BLASTN 0 /**< default threshold (blastn) */ +#define BLAST_WORD_THRESHOLD_BLASTX 12 /**< default threshold (blastx) */ +#define BLAST_WORD_THRESHOLD_TBLASTN 13 /**< default neighboring threshold + (tblastn/rpstblastn) */ +#define BLAST_WORD_THRESHOLD_TBLASTX 13 /**< default threshold (tblastx) */ +#define BLAST_WORD_THRESHOLD_MEGABLAST 0 /**< default threshold (megablast) */ + +/** default dropoff for ungapped extension; ungapped extensions + * will stop when the score for the extension has dropped from + * the current best score by at least this much + */ +#define BLAST_UNGAPPED_X_DROPOFF_PROT 7 /**< ungapped dropoff score for all + searches except blastn */ +#define BLAST_UNGAPPED_X_DROPOFF_NUCL 20 /**< ungapped dropoff score for + blastn (and megablast) */ + +/** default dropoff for preliminary gapped extensions */ +#define BLAST_GAP_X_DROPOFF_PROT 15 /**< default dropoff (all protein- + based gapped extensions) */ +#define BLAST_GAP_X_DROPOFF_NUCL 30 /**< default dropoff for non-greedy + nucleotide gapped extensions */ +#define BLAST_GAP_X_DROPOFF_GREEDY 25 /**< default dropoff for greedy + nucleotide gapped extensions */ +#define BLAST_GAP_X_DROPOFF_TBLASTX 0 /**< default dropoff for tblastx */ + +/** default bit score that will trigger gapped extension */ +#define BLAST_GAP_TRIGGER_PROT 22.0 /**< default bit score that will trigger + a gapped extension for all protein- + based searches */ +#define BLAST_GAP_TRIGGER_NUCL 27.0 /**< default bit score that will trigger + a gapped extension for blastn */ + +/** default dropoff for the final gapped extension with traceback */ +#define BLAST_GAP_X_DROPOFF_FINAL_PROT 25 /**< default dropoff (all protein- + based gapped extensions) */ +#define BLAST_GAP_X_DROPOFF_FINAL_NUCL 100 /**< default dropoff for nucleotide + gapped extensions) */ +#define BLAST_GAP_X_DROPOFF_FINAL_TBLASTX 0 /**< default dropoff for tblastx */ + +/** default reward and penalty (only applies to blastn/megablast) */ +#define BLAST_PENALTY -3 /**< default nucleotide mismatch score */ +#define BLAST_REWARD 1 /**< default nucleotide match score */ + +#define BLAST_PENALTY_MAPPER -4 +#define BLAST_REWARD_MAPPER 1 + +/** Default parameters for saving hits */ +#define BLAST_EXPECT_VALUE 10.0 /**< by default, alignments whose expect + value exceeds this number are discarded */ +#define BLAST_HITLIST_SIZE 500 /**< Number of database sequences to save hits + for */ +/** Defaults for PSI-BLAST and DELTA-BLAST options */ +#define PSI_INCLUSION_ETHRESH 0.002 /**< Inclusion threshold for PSI BLAST */ +#define PSI_PSEUDO_COUNT_CONST 0 /**< Pseudo-count constant for PSI-BLAST */ +#define DELTA_INCLUSION_ETHRESH 0.05 /**< Inclusion threshold for DELTA-BLAST */ + +/** Default genetic code for query and/or database */ +#define BLAST_GENETIC_CODE 1 /**< Use the standard genetic code for converting + groups of three nucleotide bases to protein + letters */ + +/** Default max frequency for a database word. Words with higher frequency + will be masked in the lookup table. */ +#define MAX_DB_WORD_COUNT_MAPPER 30 + +/** Default maximum insert size: distance on the subject between reads that + belong to a pair, for spliced and non-spliced alignments */ +#define MAGICBLAST_MAX_INSERT_SIZE_SPLICED 1000000 +#define MAGICBLAST_MAX_INSERT_SIZE_NONSPLICED 100000 + + +/** Value used to indicate that no IMPALA-style scaling should be performed + * when scaling a PSSM */ +extern const double kPSSM_NoImpalaScaling; + +/** Types of the lookup table */ +typedef enum { + eMBLookupTable, /**< megablast lookup table (includes both + contiguous and discontiguous megablast) */ + eSmallNaLookupTable, /**< lookup table for blastn with small query*/ + eNaLookupTable, /**< blastn lookup table */ + eAaLookupTable, /**< standard protein (blastp) lookup table */ + eCompressedAaLookupTable, /**< compressed alphabet (blastp) lookup table */ + ePhiLookupTable, /**< protein lookup table specialized for phi-blast */ + ePhiNaLookupTable, /**< nucleotide lookup table for phi-blast */ + eRPSLookupTable, /**< RPS lookup table (rpsblast and rpstblastn) */ + eIndexedMBLookupTable, /**< use database index as a lookup structure */ + eMixedMBLookupTable, /**< use when some volumes are searched with index and + some are not */ + eNaHashLookupTable /**< used for 16-base words */ +} ELookupTableType; + +/** Options needed to construct a lookup table + * Also needed: query sequence and query length. + */ +typedef struct LookupTableOptions { + double threshold; /**< Score threshold for putting words in a lookup table + (fractional values are allowed, and could be + important if there is scaling involved) */ + ELookupTableType lut_type; /**< What kind of lookup table to construct? */ + Int4 word_size; /**< Determines the size of the lookup table */ + Int4 mb_template_length; /**< Length of the discontiguous words */ + Int4 mb_template_type; /**< Type of a discontiguous word template */ + char* phi_pattern; /**< PHI-BLAST pattern */ + EBlastProgramType program_number; /**< indicates blastn, blastp, etc. */ + Uint4 stride; /**< number of words to skip after collecting each word */ + bool db_filter; /**< scan the database and include only words that appear + in the database between 1 and 9 times + (currently implemented only for MB lookuptable + and lookup table word size 16) */ + Uint1 max_db_word_count; /**< words with larger frequency in the database + will be masked in the lookup table, if the + db_filter optoion is on */ +} LookupTableOptions; + +/** Options for dust algorithm, applies only to nucl.-nucl. comparisons. + * value of less than zero means default value will be applied. + */ +typedef struct SDustOptions { + int level; + int window; + int linker; /**< min distance to link segments. */ +} SDustOptions; + + +/** Options for SEG algorithm, applies only to protein-protein comparisons. + * value of less than zero means default value will be applied. + */ +typedef struct SSegOptions { + int window; /**< initial window to trigger further work. */ + double locut; + double hicut; +} SSegOptions; + +/// Default value for repeats database filtering +#define kDefaultRepeatFilterDb "repeat/repeat_9606" + +/** Filtering options for organsim specific repeats filtering. + Currently this consist of only the db name but could be expanded + in the future to include other types of filtering or other options. + */ +typedef struct SRepeatFilterOptions { + char* database; /**< Nucleotide database for mini BLAST search. */ +} SRepeatFilterOptions; + +/** Filtering options for organism-specific filtering with Window + Masker. The taxid and filename are alternative means of choosing + which Window Masker database to use. + */ +typedef struct SWindowMaskerOptions { + int taxid; /**< Select masking database for this TaxID. */ + const char * database; /**< Use winmasker database at this location. */ +} SWindowMaskerOptions; + +/** Filtering options for mapping next-generation sequences */ +typedef struct SReadQualityOptions { + double frac_ambig; /**< Fraction of ambiguous bases */ + int entropy; /**< Dimer entropy */ +} SReadQualityOptions; + +/** All filtering options */ +typedef struct SBlastFilterOptions { + bool mask_at_hash; /**< mask query only for lookup table creation */ + SDustOptions* dustOptions; /**< low-complexity filtering for nucleotides. */ + SSegOptions* segOptions; /**< low-complexity filtering for proteins sequences + (includes translated nucleotides). */ + SRepeatFilterOptions* repeatFilterOptions; /**< for organism specific repeat filtering. */ + SWindowMaskerOptions* windowMaskerOptions; /**< organism specific filtering with window masker. */ + + SReadQualityOptions* readQualityOptions; /**< quality filtering for mapping next-generation sequences */ +} SBlastFilterOptions; + + +/** Options required for setting up the query sequence */ +typedef struct QuerySetUpOptions { + SBlastFilterOptions* filtering_options; /**< structured options for all filtering + offered from algo/blast/core for BLAST. */ + char* filter_string; /**< DEPRECATED, filtering options above. */ + + Uint1 strand_option; /**< In blastn: which strand to search: 1 = forward; + 2 = reverse; 3 = both */ + Int4 genetic_code; /**< Genetic code to use for translation, + [t]blastx only */ +} QuerySetUpOptions; + +/** Options needed for initial word finding and processing */ +typedef struct BlastInitialWordOptions { + double gap_trigger; /**< Score in bits for starting gapped extension */ + Int4 window_size; /**< Maximal allowed distance between 2 hits in case 2 + hits are required to trigger the extension */ + Int4 scan_range; /**< Maximal number of gaps allowed between 2 hits */ + double x_dropoff; /**< X-dropoff value (in bits) for the ungapped + extension */ + EBlastProgramType program_number; /**< indicates blastn, blastp, etc. */ +} BlastInitialWordOptions; + +/** The algorithm to be used for preliminary + * gapped extensions + */ +typedef enum EBlastPrelimGapExt { + eDynProgScoreOnly, /**< standard affine gapping */ + eGreedyScoreOnly, /**< Greedy extension (megaBlast) */ + eJumperWithTraceback, /**< Jumper extension (mapping) */ + eSmithWatermanScoreOnly /**< Score-only smith-waterman */ +} EBlastPrelimGapExt; + +/** The algorithm to be used for final gapped + * extensions with traceback + */ +typedef enum EBlastTbackExt { + eDynProgTbck, /**< standard affine gapping */ + eGreedyTbck, /**< Greedy extension (megaBlast) */ + eSmithWatermanTbck, /**< Smith-waterman finds optimal scores, then + ALIGN_EX to find alignment. */ + eSmithWatermanTbckFull /**< Smith-waterman to find all alignments */ +} EBlastTbackExt; + +/** Options used for gapped extension + * These include: + * a. Penalties for various types of gapping; + * b. Drop-off values for the extension algorithms tree exploration; + * c. Parameters identifying what kind of extension algorithm(s) should + * be used. + */ +typedef struct BlastExtensionOptions { + double gap_x_dropoff; /**< X-dropoff value for gapped extension (in bits) */ + double gap_x_dropoff_final;/**< X-dropoff value for the final gapped + extension (in bits) */ + EBlastPrelimGapExt ePrelimGapExt; /**< type of preliminary gapped extension (normally) for calculating + score. */ + EBlastTbackExt eTbackExt; /**< type of traceback extension. */ + Int4 compositionBasedStats; /**< mode of compositional adjustment to use; + if zero then compositional adjustment is + not used */ + Int4 unifiedP; /**< Indicates unified P values to be used in blastp or tblastn */ + + Int4 max_mismatches; /**< Maximum number of mismatches allowed for Jumper */ + + Int4 mismatch_window; /**< Widnow for counting mismatches for Jumper */ + + EBlastProgramType program_number; /**< indicates blastn, blastp, etc. */ +} BlastExtensionOptions; + +/** Options for the Best Hit HSP collection algorithm */ +typedef struct BlastHSPBestHitOptions { + double overhang; + double score_edge; +} BlastHSPBestHitOptions; + +/** Options for the HSP culling algorithm */ +typedef struct BlastHSPCullingOptions { + int max_hits; /**< Maximum number of hits per area of query. */ +} BlastHSPCullingOptions; + +typedef struct BlastHSPSubjectBestHitOptions { + unsigned int max_range_diff; +} BlastHSPSubjectBestHitOptions; + +/** Structure containing the HSP filtering/writing options */ +typedef struct BlastHSPFilteringOptions { + /** Best Hit algorithm */ + BlastHSPBestHitOptions* best_hit; + EBlastStage best_hit_stage; /*<< when to apply the best hit algorithm */ + + /** culling algorithm */ + BlastHSPCullingOptions* culling_opts; + EBlastStage culling_stage; /*<< when to apply the culling algorithm */ + + /** Subject Culling */ + BlastHSPSubjectBestHitOptions * subject_besthit_opts; +} BlastHSPFilteringOptions; + +/** Options used when evaluating and saving hits + * These include: + * a. Restrictions on the number of hits to be saved; + * b. Restrictions on the quality and positions of hits to be saved; + * c. Parameters used to evaluate the quality of hits. + */ +typedef struct BlastHitSavingOptions { + double expect_value; /**< The expect value cut-off threshold for an HSP, or + a combined hit if sum statistics is used */ + Int4 cutoff_score; /**< The (raw) score cut-off threshold */ + Int4 cutoff_score_fun[2]; /**< Coefficients x100 for the raw score cut-off + threshold as a function of query length: + x[0] + x[1] * query_length*/ + double percent_identity; /**< The percent identity cut-off threshold */ + + Int4 max_edit_distance; /**< Maximum number of mismatches and gaps */ + + Int4 hitlist_size;/**< Maximal number of database sequences to return + results for */ + Int4 hsp_num_max; /**< Maximal number of HSPs to save for one database + sequence */ + Int4 total_hsp_limit; /**< Maximal total number of HSPs to keep */ + Int4 culling_limit; /**< If the query range of an HSP is contained in + at least this many higher-scoring HSPs, throw + away the HSP as redundant (turned off if zero) */ + Int4 mask_level; /**< Only keep the highest scoring HSP when more than + one HSP overlaps the same region of the query by + more than or equal to mask_level %. -RMH- */ + + /********************************************************************/ + /* Merge all these in a structure for clarity? */ + bool do_sum_stats; /**< Force sum statistics to be used to combine HSPs, + TRUE by default for all ungapped searches and translated + gapped searches (except RPS-BLAST) */ + Int4 longest_intron; /**< The longest distance between HSPs allowed for + combining via sum statistics with uneven gaps */ + /********************************************************************/ + + Int4 min_hit_length; /**< optional minimum alignment length; alignments + not at least this long are discarded */ + Int4 min_diag_separation; /**< How many diagonals separate a hit from a substantial alignment + before it's not blocked out. Must be > 0 to be used. */ + EBlastProgramType program_number; /**< indicates blastn, blastp, etc. */ + + /** Contains options to configure the HSP filtering/writering structures + * If not set, the default HSP filtering strategy is used. + */ + BlastHSPFilteringOptions* hsp_filt_opt; + + /** Low-score option. Do not pass ungapped alignments on for later processing if + * the hitlist is already full of other alignments unless the ungapped aligment + * is above the fraction X of the least significant database match. + * zero should turn this off. + */ + double low_score_perc; + + double query_cov_hsp_perc; /**< Min query coverage hsp percentage */ + + /* Used by default hsp filtering strategy, num of best hsps to keep per subject + * seq for each query. Note that hsp_num_max should be used only to reduce memory footprint, + * it does not guarantee best hsp per query due to query concatenation + */ + Int4 max_hsps_per_subject; + + /**< Queries are paired reads, for mapping */ + bool paired; + /**< Splice HSPs for each query (for mapping RNA-Seq to a genome) */ + bool splice; + +} BlastHitSavingOptions; + +/** Scoring options block + * Used to produce the BlastScoreBlk structure + * This structure may be needed for lookup table construction (proteins only), + * and for evaluating alignments. + */ +typedef struct BlastScoringOptions { + char* matrix; /**< Name of the matrix containing all scores: needed for + finding neighboring words */ + char* matrix_path; /**< Directory path to where matrices are stored. */ + Int2 reward; /**< Reward for a match */ + Int2 penalty; /**< Penalty for a mismatch */ + bool gapped_calculation; /**< gap-free search if FALSE */ + bool complexity_adjusted_scoring; /**< Use cross_match-like complexity + adjustment on raw scores. -RMH- */ + Int4 gap_open; /**< Extra penalty for starting a gap */ + Int4 gap_extend; /**< Penalty for each gap residue */ + + /* only blastx and tblastn (When query & subj are diff) */ + bool is_ooframe; /**< Should out-of-frame gapping be used in a translated + search? */ + Int4 shift_pen; /**< Penalty for shifting a frame in out-of-frame + gapping */ + EBlastProgramType program_number; /**< indicates blastn, blastp, etc. */ +} BlastScoringOptions; + +/** Options for setting up effective lengths and search spaces. + * The values are those the user has specified to override the real sizes. + */ +typedef struct BlastEffectiveLengthsOptions { + Int8 db_length; /**< Database length to be used for statistical + calculations */ + Int4 dbseq_num; /**< Number of database sequences to be used for + statistical calculations */ + Int4 num_searchspaces; /**< Number of elements in searchsp_eff, this must be + equal to the number of contexts in the search */ + Int8 *searchsp_eff; /**< Search space to be used for statistical + calculations (one such per query context) */ +} BlastEffectiveLengthsOptions; + +/** Options used in protein BLAST only (PSI, PHI, RPS and translated BLAST) + * Some of these possibly should be transfered elsewhere + */ +typedef struct PSIBlastOptions { + /** Pseudocount constant. Needed for the computing the PSSM residue + * frequencies */ + Int4 pseudo_count; + + /*** The following options are used at the API layer to specify how the + * multiple sequence alignment is built from pairwise alignments. These + * could go in their own structure in the future. */ + + /** Minimum evalue for inclusion in PSSM calculation. Needed for the + * conversion of Seq-align into a multiple sequence alignment and for + * composition based statistics */ + double inclusion_ethresh; + + /** If set to TRUE, use the best alignment when multiple HSPs are found + * in a query-subject alignment (i.e.: HSP with the lowest e-value), else + * use all HSPs in a query-subject alignment. This option does not apply to + * the PSSM engine, it applies to the processing of pairwise sequence + * alignments to build a multiple sequence alignment structure + * @sa CPsiBlastInputData (to be implemented) + */ + bool use_best_alignment; + + /** Compatibility option for the NCBI's structure group (note + * nsg_ prefix, stands for NCBI's structure group). When set to true, the + * PSSM engine will function in the same way the C toolkit PSSM engine did + * in the structure group's cddumper application. This option should be + * set to FALSE by default as it enables the following behavior in the + * PSSM engine: + *
+     * 1) Ignores the query sequence (on certain stages of PSSM creation only)
+     * 2) Skips validation of multiple sequence alignment data
+     * 3) Disables assertions and validation in _PSICheckSequenceWeights
+     * 4) If no aligned sequences are provided in the multiple sequence
+     * alignment, NULL PSSM frequency ratios are returned and the PSSM is built
+     * based on the underlying substitution matrix.
+     * 
+ * Do not set this to TRUE unless you know what you are doing. + */ + bool nsg_compatibility_mode; + + /** Scaling factor as used in IMPALA to do the matrix rescaling. Default + * value of 1.0 means not to use it. Makemat/formatrpsdb set this value to + * 100 by default, Kappa_RedoAlignmentCore uses 32. Provided so that the + * NCBI structure group can create scaled PSSMs as the output of the PSSM + * engine. Do not change this unless you know what you are doing. + */ + double impala_scaling_factor; + + /** This turns off a validation for the multiple sequence alignment in the + * PSSM engine for unaligned positions. Needed when a multiple sequence + * alignment is provided on the command line (e.g.: -in_msa option). + */ + bool ignore_unaligned_positions; + +} PSIBlastOptions; + + +/** Options used to create the ReadDBFILE structure + * Include database name and various information for restricting the database + * to a subset. + */ +typedef struct BlastDatabaseOptions { + Int4 genetic_code; /**< Genetic code to use for translation, + tblast[nx] only */ +} BlastDatabaseOptions; + +/******************************************************************************** + + Functions to create options blocks with default values + and free them after use. + +*********************************************************************************/ + +/** Frees SDustOptions. + * @param dust_options object to free + * @return NULL pointer + */ +NCBI_XBLAST_EXPORT +SDustOptions* SDustOptionsFree(SDustOptions* dust_options); + +/** Allocates memory for SDustOptions, fills in defaults. + * @param dust_options options that are being returned [in|out] + * @return zero on sucess + */ +NCBI_XBLAST_EXPORT +Int2 SDustOptionsNew(SDustOptions* *dust_options); + +/** Frees SSegOptions. + * @param seg_options object to free [in] + * @return NULL pointer + */ +NCBI_XBLAST_EXPORT +SSegOptions* SSegOptionsFree(SSegOptions* seg_options); + +/** Allocates memory for SSegOptions, fills in defaults. [in|out] + * @param seg_options options that are being returned [in|out] + * @return zero on sucess + */ +NCBI_XBLAST_EXPORT +Int2 SSegOptionsNew(SSegOptions* *seg_options); + +/** Resets name of db for repeat filtering. + * @param repeat_options already allocated options constaining field to be reset [in|out] + * @param dbname name of the database(s) [in] + * @return zero on sucess + */ +NCBI_XBLAST_EXPORT +Int2 SRepeatFilterOptionsResetDB(SRepeatFilterOptions* *repeat_options, const char* dbname); + +/** Resets name of db for window masker filtering. + * @param winmask_options options block constaining field to be reset [in|out] + * @param dbname name of the database(s) [in] + * @return zero on sucess + */ +NCBI_XBLAST_EXPORT +Int2 SWindowMaskerOptionsResetDB(SWindowMaskerOptions ** winmask_options, + const char * dbname); + +/** Frees SRepeatFilterOptions. + * @param repeat_options object to free [in] + * @return NULL pointer + */ +NCBI_XBLAST_EXPORT +SRepeatFilterOptions* SRepeatFilterOptionsFree(SRepeatFilterOptions* repeat_options); + +/** Frees SWindowMaskerOptions. + * @param winmask_options object to free [in] + * @return NULL pointer + */ +NCBI_XBLAST_EXPORT SWindowMaskerOptions* +SWindowMaskerOptionsFree(SWindowMaskerOptions * winmask_options); + +/** Allocates memory for SRepeatFilterOptions, fills in defaults. + * @param repeat_options options that are being returned [in|out] + * @return zero on sucess + */ +NCBI_XBLAST_EXPORT +Int2 SRepeatFilterOptionsNew(SRepeatFilterOptions ** repeat_options); + +/** Allocates memory for SWindowMaskerOptions, fills in defaults. + * @param winmask_options options that are being returned [in|out] + * @return zero on sucess + */ +NCBI_XBLAST_EXPORT +Int2 SWindowMaskerOptionsNew(SWindowMaskerOptions ** winmask_options); + +/** Allocates memory for SReadQualityOptions, fills in defaults. + * @param read_quality_ptions options that are being returned [in|out] + * @return zero on sucess + */ +NCBI_XBLAST_EXPORT +Int2 SReadQualityOptionsNew(SReadQualityOptions ** read_quality_options); + +/** Frees memory for SReadQualityOptions */ +NCBI_XBLAST_EXPORT +SReadQualityOptions* SReadQualityOptionsFree( + SReadQualityOptions * read_quality_options); + + +/** Frees SBlastFilterOptions and all subservient structures. + * @param filter_options object to free + * @return NULL pointer + */ +NCBI_XBLAST_EXPORT +SBlastFilterOptions* SBlastFilterOptionsFree(SBlastFilterOptions* filter_options); + +/** Merges two sets of options together, taking the non-default one as preferred. if + * both are non-default then one or the other is taken. + * @param combined object that is returned [in|out] + * @param opt1 first set of options [in] + * @param opt2 second set of options [in] + * @return zero on success. + */ +NCBI_XBLAST_EXPORT +Int2 SBlastFilterOptionsMerge(SBlastFilterOptions** combined, const SBlastFilterOptions* opt1, + const SBlastFilterOptions* opt2); + +/** Types of filtering options. */ +typedef enum EFilterOptions { + eSeg, /**< low-complexity for proteins. */ + eDust, /**< low-complexity for nucleotides. */ + eRepeats, /**< Repeat filtering for nucleotides. */ + eDustRepeats, /**< Repeat and dust filtering for nucleotides. */ + eEmpty /**< no filtering at all. */ +} EFilterOptions; + +/** Allocates memory for SBlastFilterOptions and + * @param filter_options options that are being returned [in|out] + * @param type specify either dust or seg (now) with EFilterOptions [in] + * @return zero on sucess + */ +NCBI_XBLAST_EXPORT +Int2 SBlastFilterOptionsNew(SBlastFilterOptions* *filter_options, EFilterOptions type); + +/** Queries whether no masking is required + * @param filter_options the object to be queried [in] + * @return TRUE if no filtering is required or argument is NULL, FALSE + * otherwise + */ +NCBI_XBLAST_EXPORT +bool SBlastFilterOptionsNoFiltering(const SBlastFilterOptions* filter_options); + +/** Queries whether masking should be done only for the lookup table or for the entire search. + * @param filter_options the object to be queried [in] + * @return TRUE or FALSE, FALSE if filter_options is NULL. + */ +NCBI_XBLAST_EXPORT +bool SBlastFilterOptionsMaskAtHash(const SBlastFilterOptions* filter_options); + +/** Validates filter options to ensure that program and options are consistent + * and that options have valid values. + * @param program_number Program number (blastn, blastp, etc.) [in] + * @param filter_options options to add to [in] + * @param blast_message error or warning (optional) [out] + * @return zero on success + */ +NCBI_XBLAST_EXPORT +Int2 SBlastFilterOptionsValidate(EBlastProgramType program_number, const SBlastFilterOptions* filter_options, + Blast_Message* *blast_message); + + +/** Deallocate memory for QuerySetUpOptions. + * @param options Structure to free [in] + */ +NCBI_XBLAST_EXPORT +QuerySetUpOptions* BlastQuerySetUpOptionsFree(QuerySetUpOptions* options); + + +/** Allocate memory for QuerySetUpOptions and fill with default values. + * @param options The options that have are being returned [out] + */ +NCBI_XBLAST_EXPORT +Int2 BlastQuerySetUpOptionsNew(QuerySetUpOptions* *options); + +/** Fill non-default contents of the QuerySetUpOptions. + * @param options The options structure [in] [out] + * @param program Program number (blastn, blastp, etc.) [in] + * @param filter_string Parsable string of filtering options [in] + * @param strand_option which strand to search [in] +*/ +NCBI_XBLAST_EXPORT +Int2 BLAST_FillQuerySetUpOptions(QuerySetUpOptions* options, + EBlastProgramType program, const char *filter_string, Uint1 strand_option); + + +/** Deallocate memory for BlastInitialWordOptions. + * @param options Structure to free [in] + */ +NCBI_XBLAST_EXPORT +BlastInitialWordOptions* +BlastInitialWordOptionsFree(BlastInitialWordOptions* options); + +/** Allocate memory for BlastInitialWordOptions and fill with default values. + * @param program Program number (blastn, blastp, etc.) [in] + * @param options The options that have are being returned [out] +*/ +NCBI_XBLAST_EXPORT +Int2 +BlastInitialWordOptionsNew(EBlastProgramType program, + BlastInitialWordOptions* *options); + +/** Validate correctness of the initial word options. + * @param program_number Type of BLAST program [in] + * @param options Initial word options [in] + * @param blast_msg Describes any validation problems found [out] + * @return Validation status + */ +NCBI_XBLAST_EXPORT +Int2 +BlastInitialWordOptionsValidate(EBlastProgramType program_number, + const BlastInitialWordOptions* options, + Blast_Message* *blast_msg); + +/** Fill non-default values in the BlastInitialWordOptions structure. + * @param options The options structure [in] [out] + * @param program Program number (blastn, blastp, etc.) [in] + * @param window_size Size of a largest window between 2 words for the two-hit + * version [in] + * @param xdrop_ungapped The value of the X-dropoff for ungapped extensions [in] +*/ +NCBI_XBLAST_EXPORT +Int2 +BLAST_FillInitialWordOptions(BlastInitialWordOptions* options, + EBlastProgramType program, + Int4 window_size, double xdrop_ungapped); + +/** Deallocate memory for BlastExtensionOptions. + * @param options Structure to free [in] + */ +NCBI_XBLAST_EXPORT +BlastExtensionOptions* +BlastExtensionOptionsFree(BlastExtensionOptions* options); + +/** Allocate memory for BlastExtensionOptions and fill with default values. + * @param program Program number (blastn, blastp, etc.) [in] + * @param options The options that are being returned [out] + * @param gapped The search is gapped [in] +*/ +NCBI_XBLAST_EXPORT +Int2 +BlastExtensionOptionsNew(EBlastProgramType program, BlastExtensionOptions* *options, bool gapped); + +/** Fill non-default values in the BlastExtensionOptions structure. + * @param options The options structure [in] [out] + * @param program Program number (blastn, blastp, etc.) [in] + * @param greedy In how many stages of the search greedy alignment is + * used (values 0, 1, 2)? FIXME [in] + * @param x_dropoff X-dropoff parameter value for preliminary gapped + * extensions [in] + * @param x_dropoff_final X-dropoff parameter value for final gapped + * extensions with traceback [in] + * @todo the greedy parameter to this function is tied to the blast_driver's + * command line argument for greedy... couldn't this be EBlastPrelimGapExt? +*/ +NCBI_XBLAST_EXPORT +Int2 +BLAST_FillExtensionOptions(BlastExtensionOptions* options, + EBlastProgramType program, Int4 greedy, double x_dropoff, + double x_dropoff_final); + + + +/** Validate contents of BlastExtensionOptions. + * @param program_number Type of BLAST program [in] + * @param options Options to be validated [in] + * @param blast_msg Describes any validation problems found [out] +*/ +NCBI_XBLAST_EXPORT +Int2 BlastExtensionOptionsValidate(EBlastProgramType program_number, + const BlastExtensionOptions* options, Blast_Message* *blast_msg); + +/** Deallocate memory for BlastScoringOptions. + * @param options Structure to free [in] + */ +NCBI_XBLAST_EXPORT +BlastScoringOptions* BlastScoringOptionsFree(BlastScoringOptions* options); + +/** Allocate memory for BlastScoringOptions and fill with default values. + * @param program Program number (blastn, blastp, etc.) [in] + * @param options The options that are being returned [out] +*/ +NCBI_XBLAST_EXPORT +Int2 BlastScoringOptionsNew(EBlastProgramType program, BlastScoringOptions* *options); + +/** Fill non-default values in the BlastScoringOptions structure. + * @param options The options structure [in] [out] + * @param program Program number (blastn, blastp, etc.) [in] + * @param greedy_extension Is greedy extension algorithm used? [in] + * @param penalty Mismatch penalty score (blastn only) [in] + * @param reward Match reward score (blastn only) [in] + * @param matrix Name of the BLAST matrix (all except blastn) [in] + * @param gap_open Extra cost for opening a gap [in] + * @param gap_extend Cost of a gap [in] +*/ +NCBI_XBLAST_EXPORT +Int2 +BLAST_FillScoringOptions(BlastScoringOptions* options, EBlastProgramType program, + bool greedy_extension, Int4 penalty, Int4 reward, const char *matrix, + Int4 gap_open, Int4 gap_extend); + +/** Validate contents of BlastScoringOptions. + * @param program_number Type of BLAST program [in] + * @param options Options to be validated [in] + * @param blast_msg Describes any validation problems found [out] +*/ +NCBI_XBLAST_EXPORT +Int2 +BlastScoringOptionsValidate(EBlastProgramType program_number, + const BlastScoringOptions* options, Blast_Message* *blast_msg); + +/** Produces copy of "old" options, with new memory allocated. + * @param new_opt Contains copied BlastScoringOptions upon return [out] + * @param old_opt BlastScoringOptions to be copied [in] +*/ +NCBI_XBLAST_EXPORT +Int2 BlastScoringOptionsDup(BlastScoringOptions* *new_opt, const BlastScoringOptions* old_opt); + +/** Resets matrix name option. Automatically converts the name to upper case. + * @param opts Options structure to update. [in] [out] + * @param matrix_name New matrix name. If NULL, old matrix name is left + * as is. [in] + */ +NCBI_XBLAST_EXPORT +Int2 BlastScoringOptionsSetMatrix(BlastScoringOptions* opts, + const char* matrix_name); + + +/** Deallocate memory for BlastEffectiveLengthsOptions*. + * @param options Structure to free [in] + */ +NCBI_XBLAST_EXPORT +BlastEffectiveLengthsOptions* +BlastEffectiveLengthsOptionsFree(BlastEffectiveLengthsOptions* options); + +/** Allocate memory for BlastEffectiveLengthsOptions* and fill with + * default values. + * @param options The options that are being returned [out] + */ +NCBI_XBLAST_EXPORT +Int2 BlastEffectiveLengthsOptionsNew(BlastEffectiveLengthsOptions* *options); + +/** Return true if the search spaces is set for any of the queries in the + * search + * @param options The options to examine [in] + */ +NCBI_XBLAST_EXPORT +bool +BlastEffectiveLengthsOptions_IsSearchSpaceSet(const + BlastEffectiveLengthsOptions* + options); + +/** Fill the non-default values in the BlastEffectiveLengthsOptions structure. + * @param options The options [in] [out] + * @param dbseq_num Number of sequences in the database (if zero real value will be used) [in] + * @param db_length Total length of the database (if zero real value will be used) [in] + * @param *searchsp_eff Array of effective search spaces (the real value + * will be used for elements that are 0). If array + * contains one element, all contexts use this value. + * If array has multiple elements, the number must match + * the number of contexts in the search [in] + * @param num_searchsp The number of elements in searchsp_eff [in] + */ +NCBI_XBLAST_EXPORT +Int2 +BLAST_FillEffectiveLengthsOptions(BlastEffectiveLengthsOptions* options, + Int4 dbseq_num, Int8 db_length, + Int8 *searchsp_eff, Int4 num_searchsp); + + +/** Allocate memory for lookup table options and fill with default values. + * @param program Program number (blastn, blastp, etc.) [in] + * @param options The options that are being returned [out] + */ +NCBI_XBLAST_EXPORT +Int2 LookupTableOptionsNew(EBlastProgramType program, LookupTableOptions* *options); + + +/** Allocate memory for lookup table options and fill with default values. + * @param options The options [in] [out] + * @param program Program number (blastn, blastp, etc.) [in] + * @param is_megablast Megablast (instead of blastn) if TRUE [in] + * @param threshold Threshold value for finding neighboring words + (fractional values are allowed, though unless + the engine scales up alignment scores a fractional + threshold will be rounded down) [in] + * @param word_size Number of matched residues in an initial word [in] + */ +NCBI_XBLAST_EXPORT +Int2 +BLAST_FillLookupTableOptions(LookupTableOptions* options, + EBlastProgramType program, bool is_megablast, double threshold, + Int4 word_size); + + +/** Deallocates memory for LookupTableOptions*. + * @param options Structure to free [in] + */ +NCBI_XBLAST_EXPORT +LookupTableOptions* +LookupTableOptionsFree(LookupTableOptions* options); + +/** Validate LookupTableOptions. + * @param program_number BLAST program [in] + * @param options The options that have are being returned [in] + * @param blast_msg Describes any validation problems found [out] +*/ +NCBI_XBLAST_EXPORT +Int2 +LookupTableOptionsValidate(EBlastProgramType program_number, + const LookupTableOptions* options, Blast_Message* *blast_msg); + +/** Deallocate memory for BlastHitSavingOptions. + * @param options Structure to free [in] + */ +NCBI_XBLAST_EXPORT +BlastHitSavingOptions* +BlastHitSavingOptionsFree(BlastHitSavingOptions* options); + +/** Validate BlastHitSavingOptions + * @param program_number BLAST program [in] + * @param options The options that have are being returned [in] + * @param blast_msg Describes any validation problems found [out] +*/ + +NCBI_XBLAST_EXPORT +Int2 +BlastHitSavingOptionsValidate(EBlastProgramType program_number, + const BlastHitSavingOptions* options, Blast_Message* *blast_msg); + +/** Allocate memory for BlastHitSavingOptions. + * @param program Program number (blastn, blastp, etc.) [in] + * @param options The options that are being returned [out] + * @param gapped_calculation is this search gapped? [in] +*/ +NCBI_XBLAST_EXPORT +Int2 BlastHitSavingOptionsNew(EBlastProgramType program, + BlastHitSavingOptions** options, + bool gapped_calculation); + +/** Allocate memory for BlastHitSavingOptions. + * @param options The options [in] [out] + * @param evalue The expected value threshold [in] + * @param hitlist_size How many database sequences to save per query? [in] + * @param is_gapped is this a gapped alignment? [in] + * @param culling_limit Number of higher-scoring HSPs that must contain + * the query range of an HSP before that HSP is declared + * to be redundant (ignored if zero) [in] + * @param min_diag_separation Delete HSPs whose endpoints are at most this + * many diagonals from a higher-scoring HSP. If zero, + * delete HSPs whose query and subject ranges are + * enveloped by those of a higher-scoring HSP [in] +*/ +NCBI_XBLAST_EXPORT +Int2 +BLAST_FillHitSavingOptions(BlastHitSavingOptions* options, + double evalue, Int4 hitlist_size, + bool is_gapped, + Int4 culling_limit, + Int4 min_diag_separation); + +/** Initialize default options for PSI BLAST + * @param psi_options pointer to pointer where structure will be allocated [in] + * @return 1 in case of memory allocation failure or if psi_options is NULL, 0 + * in case of success + */ +NCBI_XBLAST_EXPORT +Int2 PSIBlastOptionsNew(PSIBlastOptions** psi_options); + +/** Validates the PSI BLAST options so that they have sane values. + * @param psi_options structure to validate [in] + * @param blast_msg Describes any validation problems found [out] + * @return 0 on success 1 on failure + */ +Int2 PSIBlastOptionsValidate(const PSIBlastOptions* psi_options, + Blast_Message** blast_msg); + +/** Deallocate PSI BLAST options */ +NCBI_XBLAST_EXPORT +PSIBlastOptions* PSIBlastOptionsFree(PSIBlastOptions* psi_options); + +/** Allocate and initialize a BlastHSPBestHitOptions structure */ +NCBI_XBLAST_EXPORT +BlastHSPBestHitOptions* BlastHSPBestHitOptionsNew(double overhang, + double score_edge); + +/** Validate the best hit algorithm parameters (if any) in the + * @param opts BlastHSPFilteringOptions structure + * @return 0 on success, else non-zero + */ +NCBI_XBLAST_EXPORT +Int2 +BlastHSPBestHitOptionsValidate(const BlastHSPFilteringOptions* opts); + +/** Deallocate a BlastHSPBestHitOptions structure + * @param opt object to be deallocated. [in] + */ +NCBI_XBLAST_EXPORT +BlastHSPBestHitOptions* BlastHSPBestHitOptionsFree(BlastHSPBestHitOptions* opt); + +/** Allocate a new object for culling options. + * @param max number of HSPs that may be aligned to one part of query [in] + */ +NCBI_XBLAST_EXPORT +BlastHSPCullingOptions* BlastHSPCullingOptionsNew(int max); + +/** Validate culling options. + * @param opts BlastHSPFilteringOptions structure + * @return 0 on success, else non-zero + */ +NCBI_XBLAST_EXPORT +Int2 +BlastHSPCullingOptionsValidate(const BlastHSPFilteringOptions* opts); + +/** Deallocates culling options structure. + * @param culling_opts object to be deallocated. [in] + */ +NCBI_XBLAST_EXPORT +BlastHSPCullingOptions* +BlastHSPCullingOptionsFree(BlastHSPCullingOptions* culling_opts); + +/** Allocate and initialize a BlastHSPFilteringOptions structure */ +NCBI_XBLAST_EXPORT +BlastHSPFilteringOptions* BlastHSPFilteringOptionsNew(); + +/** Add the best hit options. Responsibility for best_hit is taken over by the + * BlastHSPFilteringOptions + * @param filt_opts HSP filtering options [in] + * @param best_hit Best Hit algorithm options. Ownership of this is taken by + * the BlastHSPFilteringOptions structure [in|out] + */ +NCBI_XBLAST_EXPORT +Int2 +BlastHSPFilteringOptions_AddBestHit(BlastHSPFilteringOptions* filt_opts, + BlastHSPBestHitOptions** opts, + EBlastStage stage); +/** Validates the BlastHSPFilteringOptions structure */ +NCBI_XBLAST_EXPORT +Int2 +BlastHSPFilteringOptions_AddCulling(BlastHSPFilteringOptions* filt_opts, + BlastHSPCullingOptions** opts, + EBlastStage stage); + +/** Validates the BlastHSPFilteringOptions structure */ +NCBI_XBLAST_EXPORT +Int2 +BlastHSPFilteringOptionsValidate(const BlastHSPFilteringOptions* opts); + +/** Deallocate a BlastHSPFilteringOptions structure */ +NCBI_XBLAST_EXPORT +BlastHSPFilteringOptions* +BlastHSPFilteringOptionsFree(BlastHSPFilteringOptions* opts); + +/** Allocates the BlastDatabase options structure and sets the default + * database genetic code value (BLAST_GENETIC_CODE). Genetic code string in + * ncbistdaa must be populated by client code */ +NCBI_XBLAST_EXPORT +Int2 BlastDatabaseOptionsNew(BlastDatabaseOptions** db_options); + +/** Deallocate database options */ +NCBI_XBLAST_EXPORT +BlastDatabaseOptions* +BlastDatabaseOptionsFree(BlastDatabaseOptions* db_options); + +/** Initialize all the BLAST search options structures with the default + * values. + * @param blast_program Type of blast program: blastn, blastp, blastx, + * tblastn, tblastx) [in] + * @param lookup_options Lookup table options [out] + * @param query_setup_options Query options [out] + * @param word_options Initial word processing options [out] + * @param ext_options Extension options [out] + * @param hit_options Hit saving options [out] + * @param score_options Scoring options [out] + * @param eff_len_options Effective length options [out] + * @param protein_options Protein BLAST options [out] + * @param db_options BLAST database options [out] + */ +NCBI_XBLAST_EXPORT +Int2 BLAST_InitDefaultOptions(EBlastProgramType blast_program, + LookupTableOptions** lookup_options, + QuerySetUpOptions** query_setup_options, + BlastInitialWordOptions** word_options, + BlastExtensionOptions** ext_options, + BlastHitSavingOptions** hit_options, + BlastScoringOptions** score_options, + BlastEffectiveLengthsOptions** eff_len_options, + PSIBlastOptions** protein_options, + BlastDatabaseOptions** db_options); + +/** Validate all options */ +NCBI_XBLAST_EXPORT +Int2 BLAST_ValidateOptions(EBlastProgramType program_number, + const BlastExtensionOptions* ext_options, + const BlastScoringOptions* score_options, + const LookupTableOptions* lookup_options, + const BlastInitialWordOptions* word_options, + const BlastHitSavingOptions* hit_options, + Blast_Message* *blast_msg); + + + +/** Get thresholds for word-finding suggested by Stephen Altschul. + * + * @param program_number Type of blast program: blastn, blastp, blastx, + * tblastn, tblastx) [in] + * @param matrixName matrix, e.g., BLOSUM62 [in] + * @param threshold returns suggested value [in|out] + * @return zero on success + */ +NCBI_XBLAST_EXPORT +Int2 BLAST_GetSuggestedThreshold(EBlastProgramType program_number, + const char* matrixName, + double* threshold); + +/** Get window sizes for two hit algorithm suggested by Stephen Altschul. + * + * @param program_number Type of blast program: blastn, blastp, blastx, + * tblastn, tblastx) [in] + * @param matrixName matrix, e.g., BLOSUM62 [in] + * @param window_size returns suggested value [in|out] + * @return zero on success + */ +NCBI_XBLAST_EXPORT +Int2 BLAST_GetSuggestedWindowSize(EBlastProgramType program_number, + const char* matrixName, + Int4* window_size); + +/** Allocate a new object for subject besthit options. + * @params isProtein true if protein alignment [in] + */ +NCBI_XBLAST_EXPORT +BlastHSPSubjectBestHitOptions* BlastHSPSubjectBestHitOptionsNew(bool isProtein); + +/** Validate subject besthit options. + * @param opts BlastHSPFilteringOptions structure + * @return 0 on success, else non-zero + */ +NCBI_XBLAST_EXPORT +Int2 +BlastHSPSubjectBestHitOptionsValidate(const BlastHSPFilteringOptions* opts); + +/** Deallocates subject besthit structure. + * @param subject_besthit object to be deallocated. [in] + */ +NCBI_XBLAST_EXPORT +BlastHSPSubjectBestHitOptions* +BlastHSPSubjectBestHitOptionsFree(BlastHSPSubjectBestHitOptions* subject_besthit_opts); + +NCBI_XBLAST_EXPORT +Int2 +BlastHSPFilteringOptions_AddSubjectBestHit(BlastHSPFilteringOptions* filt_opts, + BlastHSPSubjectBestHitOptions** subject_besthit); + +#define DEFAULT_SUBJECT_BESTHIT_PROT_MAX_RANGE_DIFF 3 +#define DEFAULT_SUBJECT_BESTHIT_NUCL_MAX_RANGE_DIFF 3 + +#ifdef __cplusplus +} +#endif +#endif /* !__BLASTOPTIONS__ */ + diff --git a/src/lib/blast/blast_program.h b/src/lib/blast/blast_program.h index 2c5ce5e8e..43cb8a920 100644 --- a/src/lib/blast/blast_program.h +++ b/src/lib/blast/blast_program.h @@ -35,6 +35,8 @@ #define ALGO_BLAST_CORE___BLAST_PROGRAM__H #include "ncbi_std.h" +#include + #define NCBI_XBLAST_EXPORT diff --git a/src/lib/blast/blast_psi.h b/src/lib/blast/blast_psi.h new file mode 100644 index 000000000..ddf23361f --- /dev/null +++ b/src/lib/blast/blast_psi.h @@ -0,0 +1,373 @@ +/* $Id$ + * =========================================================================== + * + * PUBLIC DOMAIN NOTICE + * National Center for Biotechnology Information + * + * This software/database is a "United States Government Work" under the + * terms of the United States Copyright Act. It was written as part of + * the author's official duties as a United States Government employee and + * thus cannot be copyrighted. This software/database is freely available + * to the public for use. The National Library of Medicine and the U.S. + * Government have not placed any restriction on its use or reproduction. + * + * Although all reasonable efforts have been taken to ensure the accuracy + * and reliability of the software and data, the NLM and the U.S. + * Government do not and cannot warrant the performance or results that + * may be obtained by using this software or data. The NLM and the U.S. + * Government disclaim all warranties, express or implied, including + * warranties of performance, merchantability or fitness for any particular + * purpose. + * + * Please cite the author in any work or product based on this material. + * + * =========================================================================== + * + * Author: Christiam Camacho + * + */ + +/** @file blast_psi.h + * High level definitions and declarations for the PSSM engine of PSI-BLAST. + */ + +#ifndef ALGO_BLAST_CORE___BLAST_PSI__H +#define ALGO_BLAST_CORE___BLAST_PSI__H + +#include "ncbi_std.h" +#include "blast_export.h" +#include "blast_options.h" +#include "blast_stat.h" +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/** Structure to describe the characteristics of a position in the multiple + * sequence alignment data structure + */ +typedef struct PSIMsaCell { + Uint1 letter; /**< Preferred letter at this position, in + ncbistdaa encoding */ + bool is_aligned; /**< Is this letter part of the alignment? */ +} PSIMsaCell; + +/** Structure representing the dimensions of the multiple sequence alignment + * data structure */ +typedef struct PSIMsaDimensions { + Uint4 query_length; /**< Length of the query */ + Uint4 num_seqs; /**< Number of distinct sequences aligned with the + query (does not include the query) */ +} PSIMsaDimensions; + +#ifdef DEBUG_PSSM_ENGINE +/** Sequence information structure */ +typedef struct PSISeqInfo { + int gi; /**< Sequence GI */ + double bit_score; /**< Bit score of this sequence aligned with query*/ + double evalue; /**< E-value of this sequence aligned with query*/ +} PSISeqInfo; +#endif /* DEBUG_PSSM_ENGINE */ + +/** Multiple sequence alignment (msa) data structure containing the raw data + * needed by the PSSM engine to create a PSSM. By convention, the first row of + * the data field contains the query sequence */ +typedef struct PSIMsa { + PSIMsaDimensions* dimensions; /**< dimensions of the msa */ + PSIMsaCell** data; /**< actual data, dimensions are + (dimensions->num_seqs+1) by + (dimensions->query_length) */ +#ifdef DEBUG_PSSM_ENGINE + PSISeqInfo* seqinfo; /** sequence information for a row of the + multiple sequence alignment, length is + dimensions->num_seqs+1 */ + +#endif /* DEBUG_PSSM_ENGINE */ +} PSIMsa; + +/** Allocates and initializes the multiple sequence alignment data structure + * for use as input to the PSSM engine. + * @param dimensions dimensions of multiple sequence alignment data structure + * to allocate [in] + * @return allocated PSIMsa structure or NULL if out of memory. + */ +NCBI_XBLAST_EXPORT +PSIMsa* +PSIMsaNew(const PSIMsaDimensions* dimensions); + +/** Deallocates the PSIMsa structure + * @param msa multiple sequence alignment structure to deallocate [in] + * @return NULL + */ +NCBI_XBLAST_EXPORT +PSIMsa* +PSIMsaFree(PSIMsa* msa); + +/******************************************************************* +* Data structures for computing PSSM from using Conserved Domains +* +*/ + +/** Data needed for PSSM computation stored in MSA cell for single column in + * CD aligned to a position in the query */ +typedef struct PSICdMsaCellData { + + double* wfreqs; /**< Frequencies for each residue in + CD column */ + + double iobsr; /**< Effective number of independent + observations in a CD column */ +} PSICdMsaCellData; + +/** Alignment cell that represents one column of CD aligned to a position + * in the query*/ +typedef struct PSICdMsaCell { + Uint1 is_aligned; /**< Does this cell represent column aligned + to a CD */ + + PSICdMsaCellData* data; /**< Data needed for PSSM computation */ +} PSICdMsaCell; + + +/** Data structure representing multiple alignemnt of CDs and query sequence + along with data needed for PSSM computation */ +typedef struct PSICdMsa { + unsigned char* query; /**< Query sequence as Ncbistdaa */ + PSIMsaDimensions* dimensions; /**< Query length and number of aligned cds */ + + PSICdMsaCell **msa; /**< Multiple alignment of CDs */ +} PSICdMsa; + + +#ifdef DEBUG_PSSM_ENGINE +NCBI_XBLAST_EXPORT +void PrintMsa(const char* filename, const PSIMsa* msa); +NCBI_XBLAST_EXPORT +void PrintMsaFP(FILE* fp, const PSIMsa* msa); +#endif /* DEBUG_PSSM_ENGINE */ + +/** This is the main return value from the PSSM engine */ +typedef struct PSIMatrix { + Uint4 ncols; /**< Number of columns in PSSM (query_length) */ + Uint4 nrows; /**< Number of rows in PSSM (alphabet_size) */ + int** pssm; /**< Position-specific score matrix */ + double lambda; /**< Lambda Karlin-Altschul parameter */ + double kappa; /**< Kappa Karlin-Altschul parameter */ + double h; /**< H Karlin-Altschul parameter */ + double ung_lambda; /**< Ungapped Lambda Karlin-Altschul parameter */ + double ung_kappa; /**< Ungapped Kappa Karlin-Altschul parameter */ + double ung_h; /**< Ungapped H Karlin-Altschul parameter */ +} PSIMatrix; + +/** Allocates a new PSIMatrix structure + * @param query_length number of columns allocated for the PSSM [in] + * @param alphabet_size number of rows allocated for the PSSM [in] + * @return pointer to allocated PSIMatrix structure or NULL if out of memory + */ +NCBI_XBLAST_EXPORT +PSIMatrix* +PSIMatrixNew(Uint4 query_length, Uint4 alphabet_size); + +/** Deallocates the PSIMatrix structure passed in. + * @param matrix structure to deallocate [in] + * @return NULL + */ +NCBI_XBLAST_EXPORT +PSIMatrix* +PSIMatrixFree(PSIMatrix* matrix); + +/** Structure to allow requesting various diagnostics data to be collected by + * PSSM engine */ +typedef struct PSIDiagnosticsRequest { + bool information_content; /**< request information content */ + bool residue_frequencies; /**< request observed residue + frequencies */ + bool weighted_residue_frequencies; /**< request observed weighted + residue frequencies */ + bool frequency_ratios; /**< request frequency ratios */ + bool gapless_column_weights; /**< request gapless column weights + */ + bool sigma; /**< request sigma */ + bool interval_sizes; /**< request interval sizes */ + bool num_matching_seqs; /**< request number of matching + sequences */ + bool independent_observations; /**< request number of independent + observations */ + +} PSIDiagnosticsRequest; + +/** This structure contains the diagnostics information requested using the + * PSIDiagnosticsRequest structure */ +typedef struct PSIDiagnosticsResponse { + double* information_content; /**< position information content + (query_length elements)*/ + Uint4** residue_freqs; /**< observed residue frequencies + per position of the PSSM + (Dimensions are query_length by + alphabet_size) */ + double** weighted_residue_freqs; /**< Weighted observed residue + frequencies per position of the + PSSM. (Dimensions are query_length + by alphabet_size) */ + double** frequency_ratios; /**< PSSM's frequency ratios + (Dimensions are query_length by + alphabet_size) */ + double* gapless_column_weights; /**< Weights for columns without + gaps (query_length elements) */ + double* sigma; /**< sigma (query_length elements) */ + Uint4* interval_sizes; /**< interval sizes of aligned + regions (query_length elements) */ + Uint4* num_matching_seqs; /**< number of matching sequences + per query position (query_length + elements) */ + Uint4 query_length; /**< Specifies the number of + positions in the PSSM */ + Uint4 alphabet_size; /**< Specifies length of alphabet */ + + double* independent_observations; /**< Effective number of + observations per column */ +} PSIDiagnosticsResponse; + +/** Allocates a PSIDiagnosticsRequest structure, setting all fields to false + * @return newly allocated structure or NULL in case of memory allocation + * failure + */ +NCBI_XBLAST_EXPORT +PSIDiagnosticsRequest* +PSIDiagnosticsRequestNew(void); + +/** Allocates a PSIDiagnosticsRequest structure, setting fields to their + * default values for their use in the context of the PSI-BLAST application. + * @param save_ascii_pssm corresponds to the command line argument to save the + * PSSM in ASCII format [in] + * @return newly allocated structure or NULL in case of memory allocation + * failure + */ +NCBI_XBLAST_EXPORT +PSIDiagnosticsRequest* +PSIDiagnosticsRequestNewEx(bool save_ascii_pssm); + +/** Deallocates the PSIDiagnosticsRequest structure passed in + * @param diags_request structure to deallocate [in] + * @return NULL + */ +NCBI_XBLAST_EXPORT +PSIDiagnosticsRequest* +PSIDiagnosticsRequestFree(PSIDiagnosticsRequest* diags_request); + +/** Allocates a new PSI-BLAST diagnostics structure based on which fields of + * the PSIDiagnosticsRequest structure are TRUE. Note: this is declared + * here for consistency - this does not need to be called by client code of + * this API, it is called in the PSICreatePssm* functions to allocate the + * diagnostics response structure. + * @param query_length length of the query sequence [in] + * @param alphabet_size length of the alphabet [in] + * @param request diagnostics to retrieve from PSSM engine [in] + * @return pointer to allocated PSIDiagnosticsResponse or NULL if dimensions or + * request are NULL + */ +NCBI_XBLAST_EXPORT +PSIDiagnosticsResponse* +PSIDiagnosticsResponseNew(Uint4 query_length, Uint4 alphabet_size, + const PSIDiagnosticsRequest* request); + +/** Deallocates the PSIDiagnosticsResponse structure passed in. + * @param diags structure to deallocate [in] + * @return NULL + */ +NCBI_XBLAST_EXPORT +PSIDiagnosticsResponse* +PSIDiagnosticsResponseFree(PSIDiagnosticsResponse* diags); + +/****************************************************************************/ + +/** Main entry point to core PSSM engine to calculate the PSSM. + * @param msap multiple sequence alignment data structure [in] + * @param options options to the PSSM engine [in] + * @param sbp BLAST score block structure [in|out] + * @param pssm PSSM and statistical information (the latter is also returned + * in the sbp->kbp_gap_psi[0]) + * @return PSI_SUCCESS on success, otherwise one of the PSIERR_* constants + */ +NCBI_XBLAST_EXPORT +int +PSICreatePssm(const PSIMsa* msap, + const PSIBlastOptions* options, + BlastScoreBlk* sbp, + PSIMatrix** pssm); + +/** Main entry point to core PSSM engine which allows to request diagnostics + * information. + * @param msap multiple sequence alignment data structure [in] + * @param options options to the PSSM engine [in] + * @param sbp BLAST score block structure [in|out] + * @param request diagnostics information request [in] + * @param pssm PSSM and statistical information (the latter is also returned + * in the sbp->kbp_gap_psi[0]) [out] + * @param diagnostics diagnostics information response, expects a pointer to an + * uninitialized structure which will be populated with data requested in + * requests [in|out] + * @return PSI_SUCCESS on success, otherwise one of the PSIERR_* constants + */ +NCBI_XBLAST_EXPORT +int +PSICreatePssmWithDiagnostics(const PSIMsa* msap, + const PSIBlastOptions* options, + BlastScoreBlk* sbp, + const PSIDiagnosticsRequest* request, + PSIMatrix** pssm, + PSIDiagnosticsResponse** diagnostics); + +/** Main entry point to core PSSM engine for computing CDD-based PSSMs + * @param cd_msa information about CDs that match to query sequence [in] + * @param options options to PSSM engine [in] + * @param sbp BLAST score block structure [in|out] + * @param request diagnostics information request [in] + * @param pssm PSSM [out] + * @param diagnostics diagnostics information response, expects a pointer to + * uninitialized structure [in|out] + */ +int +PSICreatePssmFromCDD(const PSICdMsa* cd_msa, + const PSIBlastOptions* options, + BlastScoreBlk* sbp, + const PSIDiagnosticsRequest* request, + PSIMatrix** pssm, + PSIDiagnosticsResponse** diagnostics); + + + +/** Top-level function to create a PSSM given a matrix of frequency ratios + * and perform scaling on the resulting PSSM (i.e.: performs the last two + * stages of the algorithm) + * Note that no diagnostics can be returned as those are calculated in earlier + * stages of the algorithm. + * @param query query sequence in ncbistdaa format, no sentinels needed [in] + * @param query_length length of the query sequence [in] + * @param sbp BLAST score block structure [in|out] + * @param freq_ratios matrix of frequency ratios, dimensions are query_length + * by BLASTAA_SIZE [in] + * @param impala_scaling_factor scaling factor used in IMPALA-style scaling if + * its value is NOT kPSSM_NoImpalaScaling (otherwise it performs standard + * PSI-BLAST scaling) [in] + * @param pssm PSSM and statistical information [in|out] + * @return PSI_SUCCESS on success, otherwise one of the PSIERR_* constants + * @todo FIXME change scalePosMatrix (blast_kappa.c) to use this function + */ +NCBI_XBLAST_EXPORT +int +PSICreatePssmFromFrequencyRatios(const Uint1* query, + Uint4 query_length, + BlastScoreBlk* sbp, + double** freq_ratios, + double impala_scaling_factor, + PSIMatrix** pssm); + +#ifdef __cplusplus +} +#endif + +#endif /* !ALGO_BLAST_CORE__BLAST_PSI__H */ + diff --git a/src/lib/blast/blast_psi_priv.h b/src/lib/blast/blast_psi_priv.h new file mode 100644 index 000000000..ebc4e2852 --- /dev/null +++ b/src/lib/blast/blast_psi_priv.h @@ -0,0 +1,758 @@ +/* $Id$ + * =========================================================================== + * + * PUBLIC DOMAIN NOTICE + * National Center for Biotechnology Information + * + * This software/database is a "United States Government Work" under the + * terms of the United States Copyright Act. It was written as part of + * the author's official duties as a United States Government employee and + * thus cannot be copyrighted. This software/database is freely available + * to the public for use. The National Library of Medicine and the U.S. + * Government have not placed any restriction on its use or reproduction. + * + * Although all reasonable efforts have been taken to ensure the accuracy + * and reliability of the software and data, the NLM and the U.S. + * Government do not and cannot warrant the performance or results that + * may be obtained by using this software or data. The NLM and the U.S. + * Government disclaim all warranties, express or implied, including + * warranties of performance, merchantability or fitness for any particular + * purpose. + * + * Please cite the author in any work or product based on this material. + * + * =========================================================================== + * + * Author: Alejandro Schaffer, ported by Christiam Camacho + * + */ + +/** @file blast_psi_priv.h + * Private interface for Position Iterated BLAST API, contains the + * PSSM generation engine. + * + *
+ * Calculating PSSMs from Seq-aligns is a multi-stage process. These stages
+ * include:
+ * 1) Processing the Seq-align
+ *      Examine alignment and extract information about aligned characters,
+ *      performed at the API level
+ * 2) Purge biased sequences: construct M multiple sequence alignment as
+ *      described in page 3395[1] - performed at the core level; custom
+ *      selection of sequences should be performed at the API level.
+ * 3) Compute extents of the alignment: M sub C as described in page 3395[1]
+ * 4) Compute sequence weights
+ * 5) Compute residue frequencies
+ * 6) Convert residue frequencies to PSSM
+ * 7) Scale the resulting PSSM
+ * 
+ */ + +#ifndef ALGO_BLAST_CORE___BLAST_PSI_PRIV__H +#define ALGO_BLAST_CORE___BLAST_PSI_PRIV__H + +#include "ncbi_std.h" +#include "blast_stat.h" +#include "blast_psi.h" +#include "matrix_freq_ratios.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************/ +/* Extern declarations for constants (defined in blast_psi_priv.c) */ + +/** Percent identity threshold for discarding near-identical matches */ +NCBI_XBLAST_EXPORT +extern const double kPSINearIdentical; + +/** Percent identity threshold for discarding identical matches */ +NCBI_XBLAST_EXPORT +extern const double kPSIIdentical; + +/** Index into multiple sequence alignment structure for the query sequence */ +NCBI_XBLAST_EXPORT +extern const unsigned int kQueryIndex; + +/** Small constant to test against 0 */ +NCBI_XBLAST_EXPORT +extern const double kEpsilon; + +/** Successor to POSIT_SCALE_FACTOR */ +NCBI_XBLAST_EXPORT +extern const int kPSIScaleFactor; + +/** Constant used in scaling PSSM routines: Successor to POSIT_PERCENT */ +NCBI_XBLAST_EXPORT +extern const double kPositScalingPercent; +/** Constant used in scaling PSSM routines: Successor to POSIT_NUM_ITERATIONS */ +NCBI_XBLAST_EXPORT +extern const Uint4 kPositScalingNumIterations; + +/****************************************************************************/ +/* Matrix utility functions */ + +/** Generic 2 dimensional matrix allocator. + * Allocates a ncols by nrows matrix with cells of size data_type_sz. Must be + * freed using x_DeallocateMatrix + * @param ncols number of columns in matrix [in] + * @param nrows number of rows in matrix [in] + * @param data_type_sz size of the data type (in bytes) to allocate for each + * element in the matrix [in] + * @return pointer to allocated memory or NULL in case of failure + */ +NCBI_XBLAST_EXPORT +void** +_PSIAllocateMatrix(unsigned int ncols, unsigned int nrows, + unsigned int data_type_sz); + +/** Generic 2 dimensional matrix deallocator. + * Deallocates the memory allocated by x_AllocateMatrix + * @param matrix matrix to deallocate [in] + * @param ncols number of columns in the matrix [in] + * @return NULL + */ +NCBI_XBLAST_EXPORT +void** +_PSIDeallocateMatrix(void** matrix, unsigned int ncols); + +/** Copies src matrix into dest matrix, both of which must be int matrices with + * dimensions ncols by nrows + * @param dest Destination matrix [out] + * @param src Source matrix [in] + * @param ncols Number of columns to copy [in] + * @param nrows Number of rows to copy [in] + */ +NCBI_XBLAST_EXPORT +void +_PSICopyMatrix_int(int** dest, int** src, + unsigned int ncols, unsigned int nrows); + +/** Copies src matrix into dest matrix, both of which must be double matrices + * with dimensions ncols by nrows + * @param dest Destination matrix [out] + * @param src Source matrix [in] + * @param ncols Number of columns to copy [in] + * @param nrows Number of rows to copy [in] + */ +NCBI_XBLAST_EXPORT +void +_PSICopyMatrix_double(double** dest, double** src, + unsigned int ncols, unsigned int nrows); + +/****************************************************************************/ +/* Structure declarations */ + +/** Compact version of the PSIMsaCell structure */ +typedef struct _PSIPackedMsaCell { + unsigned int letter:7; /**< Preferred letter at this position, in + ncbistdaa encoding */ + unsigned int is_aligned:1; /**< Is this letter part of the alignment? */ +} _PSIPackedMsaCell; + +/** Compact version of PSIMsa structure */ +typedef struct _PSIPackedMsa { + PSIMsaDimensions* dimensions; /**< dimensions of the msa */ + _PSIPackedMsaCell** data; /**< actual data, dimensions are + (dimensions->num_seqs+1) by + (dimensions->query_length) */ + bool* use_sequence; /**< used to indicate whether a + sequence should be used for + further processing by the engine + (length: num_seqs + 1) */ +}_PSIPackedMsa; + +/** Allocates and initializes the compact version of the PSIMsa structure + * (makes a deep copy) for internal use by the PSSM engine. + * @param msa multiple sequence alignment data structure provided by the user + * [in] + * @return newly allocated structure or NULL in case of memory allocation + * failure + */ +NCBI_XBLAST_EXPORT +_PSIPackedMsa* +_PSIPackedMsaNew(const PSIMsa* msa); + +/** Deallocates the _PSIMsa data structure. + * @param msa multiple sequence alignment data structure to deallocate [in] + * @return NULL + */ +NCBI_XBLAST_EXPORT +_PSIPackedMsa* +_PSIPackedMsaFree(_PSIPackedMsa* msa); + +/** Retrieve the number of aligned sequences in the compact multiple sequence + * alignment + * @param msa multiple sequence alignment data structure to deallocate [in] + */ +NCBI_XBLAST_EXPORT +unsigned int +_PSIPackedMsaGetNumberOfAlignedSeqs(const _PSIPackedMsa* msa); + + +/** Internal data structure to represent a position in the multiple sequence + * alignment data structure @sa _PSIMsa */ +typedef struct _PSIMsaCell { + unsigned int letter:7; /**< Preferred letter at this position */ + unsigned int is_aligned:1; /**< Is this letter part of the alignment? */ + SSeqRange extents; /**< Extents of this aligned position */ +} _PSIMsaCell; + +/** Internal multiple alignment data structure used by the PSSM engine */ +typedef struct _PSIMsa { + PSIMsaDimensions* dimensions; /**< dimensions of field below */ + _PSIMsaCell** cell; /**< multiple sequence alignment + matrix (dimensions: query_length + x num_seqs + 1) */ + Uint1* query; /**< query sequence (length: + query_length) */ + Uint4** residue_counts; /**< matrix to keep track of the + raw residue counts at each + position of the multiple sequence + alignment (dimensions: + query_length x alphabet_size) */ + Uint4 alphabet_size; /**< number of elements in + alphabet */ + Uint4* num_matching_seqs; /**< number of sequences aligned at + a given position in the multiple + sequence alignment (length: + query_length). Corresponds to + posit.h:posSearch->posCount */ +} _PSIMsa; + +/** Allocates and initializes the internal version of the PSIMsa structure + * (makes a deep copy) for internal use by the PSSM engine. + * @param packed_msa compact multiple sequence alignment data structure [in] + * @param alphabet_size number of elements in the alphabet that makes up the + * aligned characters in the multiple sequence alignment [in] + * @return newly allocated structure or NULL in case of memory allocation + * failure + */ +NCBI_XBLAST_EXPORT +_PSIMsa* +_PSIMsaNew(const _PSIPackedMsa* packed_msa, Uint4 alphabet_size); + +/** Deallocates the _PSIMsa data structure. + * @param msa multiple sequence alignment data structure to deallocate [in] + * @return NULL + */ +NCBI_XBLAST_EXPORT +_PSIMsa* +_PSIMsaFree(_PSIMsa* msa); + +/** Internal representation of a PSSM in various stages of its creation and its + * dimensions. */ +typedef struct _PSIInternalPssmData { + Uint4 ncols; /**< number of columns (query_length) */ + Uint4 nrows; /**< number of rows (alphabet_size) */ + int** pssm; /**< PSSM (scores) */ + int** scaled_pssm; /**< scaled PSSM (scores) */ + double** freq_ratios; /**< frequency ratios */ + double* pseudocounts; /**< pseudocount constant for each column */ +} _PSIInternalPssmData; + +/** Allocates a new _PSIInternalPssmData structure. + * @param query_length number of columns for the PSSM [in] + * @param alphabet_size number of rows for the PSSM [in] + * @return newly allocated structure or NULL in case of memory allocation + * failure + */ +NCBI_XBLAST_EXPORT +_PSIInternalPssmData* +_PSIInternalPssmDataNew(Uint4 query_length, Uint4 alphabet_size); + +/** Deallocates the _PSIInternalPssmData structure. + * @param pssm data structure to deallocate [in] + * @return NULL + */ +NCBI_XBLAST_EXPORT +_PSIInternalPssmData* +_PSIInternalPssmDataFree(_PSIInternalPssmData* pssm); + +/** This structure keeps track of the regions aligned between the query + * sequence and those that were not purged. It is used when calculating the + * sequence weights (replaces posExtents in old code) */ +typedef struct _PSIAlignedBlock { + SSeqRange* pos_extnt; /**< Dynamically allocated array of size + query_length to keep track of the extents + of each aligned position */ + + Uint4* size; /**< Dynamically allocated array of size + query_length that contains the size of the + intervals in the array above */ +} _PSIAlignedBlock; + +/** Allocates and initializes the _PSIAlignedBlock structure. + * @param query_length length of the query sequence of the multiple + * sequence alignment [in] + * @return newly allocated structure or NULL in case of memory allocation + * failure + */ +NCBI_XBLAST_EXPORT +_PSIAlignedBlock* +_PSIAlignedBlockNew(Uint4 query_length); + +/** Deallocates the _PSIAlignedBlock structure. + * @param aligned_blocks data structure to deallocate [in] + * @return NULL + */ +NCBI_XBLAST_EXPORT +_PSIAlignedBlock* +_PSIAlignedBlockFree(_PSIAlignedBlock* aligned_blocks); + +/** Internal data structure to keep computed sequence weights */ +typedef struct _PSISequenceWeights { + /* Some notes on how elements of this structures corresponds to elements + of structures defined in posit.h: + + _PSISequenceWeights->match_weights is the same as posSearch->posMatchWeights + _PSISequenceWeights->gapless_column_weights is the same as + posSearch->posGaplessColumnWeights + _PSISequenceWeights->norm_seq_weights is the same as posSearch->posA + _PSISequenceWeights->row_sigma is the same as posSearch->posRowSigma + */ + double** match_weights; /**< weighted observed residue frequencies (f_i + in 2001 paper). (dimensions: query_length + x BlastScoreBlk's alphabet_size) */ + Uint4 match_weights_size; /**< kept for help deallocate the field above */ + + double* norm_seq_weights; /**< Stores the normalized sequence weights + (length: num_seqs + 1) */ + double* row_sigma; /**< array of length num_seqs + 1 */ + /* Sigma: number of different characters occurring in matches within a + * multi-alignment block - FIXME: why is it a double? */ + double* sigma; /**< array of length query_length */ + + double* std_prob; /**< standard amino acid probabilities */ + + /* This fields is required for important diagnostic output, they are + * copied into diagnostics structure */ + double* gapless_column_weights; /**< FIXME */ + int** posDistinctDistrib; /**< For position i, how many positions in its block + have j distinct letters. Copied from + posit.h:posSearchItems.posDistinctDistrib */ + Uint4 posDistinctDistrib_size; /**< Kept to deallocate field above. */ + int* posNumParticipating; /**< number of sequences at each position. Copied from + posit.h:posSearchItems.posNumParticipating */ + double* independent_observations; /**< Number of independent sequences + per column */ + +} _PSISequenceWeights; + +/** Allocates and initializes the _PSISequenceWeights structure. + * @param dims structure containing the multiple sequence alignment dimensions + * [in] + * @param sbp score block structure initialized for the scoring system used + * with the query sequence [in] + * @return newly allocated structure or NULL in case of memory allocation + * failure + */ +NCBI_XBLAST_EXPORT +_PSISequenceWeights* +_PSISequenceWeightsNew(const PSIMsaDimensions* dims, const BlastScoreBlk* sbp); + +/** Deallocates the _PSISequenceWeights structure. + * @param seq_weights data structure to deallocate [in] + * @return NULL + */ +NCBI_XBLAST_EXPORT +_PSISequenceWeights* +_PSISequenceWeightsFree(_PSISequenceWeights* seq_weights); + +/* Return values for internal PSI-BLAST functions */ + +/** Successful operation */ +#define PSI_SUCCESS (0) +/** Bad parameter used in function */ +#define PSIERR_BADPARAM (-1) +/** Out of memory */ +#define PSIERR_OUTOFMEM (-2) +/** Sequence weights do not add to 1 */ +#define PSIERR_BADSEQWEIGHTS (-3) +/** No frequency ratios were found for the given scoring matrix */ +#define PSIERR_NOFREQRATIOS (-4) +/** Positive average score found when scaling matrix */ +#define PSIERR_POSITIVEAVGSCORE (-5) +/** After purge stage of PSSM creation, no sequences are left */ +#define PSIERR_NOALIGNEDSEQS (-6) +/** GAP residue found in query sequence */ +#define PSIERR_GAPINQUERY (-7) +/** Found an entire column with no participating sequences */ +#define PSIERR_UNALIGNEDCOLUMN (-8) +/** Found an entire column full of GAP residues */ +#define PSIERR_COLUMNOFGAPS (-9) +/** Found flanking gap at start of alignment */ +#define PSIERR_STARTINGGAP (-10) +/** Found flanking gap at end of alignment */ +#define PSIERR_ENDINGGAP (-11) +/** Errors in conserved domain profile */ +#define PSIERR_BADPROFILE (-12) +/** Unknown error */ +#define PSIERR_UNKNOWN (-255) + +/****************************************************************************/ +/* Function prototypes for the various stages of the PSSM generation engine */ + +/** Main function for keeping only those selected sequences for PSSM + * construction (stage 2). After this function the multiple sequence alignment + * data will not be modified. + * @sa implementation of PSICreatePssmWithDiagnostics + * @param msa multiple sequence alignment data structure [in] + * @return PSIERR_BADPARAM if alignment is NULL; PSI_SUCCESS otherwise + */ +NCBI_XBLAST_EXPORT +int +_PSIPurgeBiasedSegments(_PSIPackedMsa* msa); + +/** Main validation function for multiple sequence alignment structure. Should + * be called after _PSIPurgeBiasedSegments. + * @param msa multiple sequence alignment data structure [in] + * @param ignored_unaligned_positions determines whether the unaligned + * positions test should be performend or not [in] + * @return One of the errors defined above if validation fails or bad + * parameter is passed in, else PSI_SUCCESS + */ +NCBI_XBLAST_EXPORT +int +_PSIValidateMSA(const _PSIMsa* msa, bool ignored_unaligned_positions); + +/** Main function to compute aligned blocks' properties for each position + * within multiple alignment (stage 3) + * Corresponds to posit.c:posComputeExtents + * @param msa multiple sequence alignment data structure [in] + * @param aligned_block data structure describing the aligned blocks' + * properties for each position of the multiple sequence alignment [out] + * @return PSIERR_BADPARAM if arguments are NULL + * PSI_SUCCESS otherwise + */ +NCBI_XBLAST_EXPORT +int +_PSIComputeAlignmentBlocks(const _PSIMsa* msa, + _PSIAlignedBlock* aligned_block); + +/** Main function to calculate the sequence weights. Should be called with the + * return value of PSIComputeAlignmentBlocks (stage 4) + * Corresponds to posit.c:posComputeSequenceWeights + * @param msa multiple sequence alignment data structure [in] + * @param aligned_blocks data structure describing the aligned blocks' + * properties for each position of the multiple sequence alignment [in] + * @param nsg_compatibility_mode set to true to emulate the structure group's + * use of PSSM engine in the cddumper application. By default should be FALSE + * [in] + * @param seq_weights data structure containing the data needed to compute the + * sequence weights [out] + * @return PSIERR_BADPARAM if arguments are NULL, PSIERR_OUTOFMEM in case of + * memory allocation failure, PSIERR_BADSEQWEIGHTS if the sequence weights fail + * to add up to 1.0, PSI_SUCCESS otherwise + */ +NCBI_XBLAST_EXPORT +int +_PSIComputeSequenceWeights(const _PSIMsa* msa, + const _PSIAlignedBlock* aligned_blocks, + bool nsg_compatibility_mode, + _PSISequenceWeights* seq_weights); + +/** Main function to calculate CD weights and combine weighted residue counts + * from matched CDs + * @param cd_msa multiple alignment of conserved domains data structure [in] + * @param sbp BLAST score block [in] + * @param options CDD-related options [in] + * @param seq_weights data structure with CD frequencies [out] + */ +NCBI_XBLAST_EXPORT +int +_PSIComputeFrequenciesFromCDs(const PSICdMsa* cd_msa, + BlastScoreBlk* sbp, + const PSIBlastOptions* options, + _PSISequenceWeights* seq_weights); + + +/** Main function to compute the PSSM's frequency ratios (stage 5). + * Implements formula 2 in Nucleic Acids Research, 2001, Vol 29, No 14. + * Corresponds to posit.c:posComputePseudoFreqs + * @param msa multiple sequence alignment data structure [in] + * @param seq_weights data structure containing the data needed to compute the + * sequence weights [in] + * @param sbp score block structure initialized for the scoring system used + * with the query sequence [in] + * @param aligned_blocks data structure describing the aligned blocks' + * properties for each position of the multiple sequence alignment [in] + * @param pseudo_count pseudo count constant [in] + * @param nsg_compatibility_mode set to true to emulate the structure group's + * use of PSSM engine in the cddumper application. By default should be FALSE + * @param internal_pssm PSSM being computed [out] + * @return PSIERR_BADPARAM if arguments are NULL, PSI_SUCCESS otherwise + */ +NCBI_XBLAST_EXPORT +int +_PSIComputeFreqRatios(const _PSIMsa* msa, + const _PSISequenceWeights* seq_weights, + const BlastScoreBlk* sbp, + const _PSIAlignedBlock* aligned_blocks, + Int4 pseudo_count, + bool nsg_compatibility_mode, + _PSIInternalPssmData* internal_pssm); + +/** Main function to compute CD-based PSSM's frequency ratios + * @param cd_msa multiple alignment of CDs [in] + * @param seq_weights contains weighted residue frequencies and effective number + * of observations [in] + * @param sbp initialized score block data structure [in] + * @param pseudo_count pseudo count constant [in] + * @param internal_pssm PSSM [out] + * @return status + */ +NCBI_XBLAST_EXPORT +int +_PSIComputeFreqRatiosFromCDs(const PSICdMsa* cd_msa, + const _PSISequenceWeights* seq_weights, + const BlastScoreBlk* sbp, + Int4 pseudo_count, + _PSIInternalPssmData* internal_pssm); + + +/** Converts the PSSM's frequency ratios obtained in the previous stage to a + * PSSM of scores. (stage 6) + * @param internal_pssm PSSM being computed [in|out] + * @param query query sequence in ncbistdaa encoding. The length of this + * sequence is read from internal_pssm->ncols [in] + * @param sbp score block structure initialized for the scoring system used + * with the query sequence [in] + * @param std_probs array containing the standard residue probabilities [in] + * @return PSIERR_BADPARAM if arguments are NULL, PSI_SUCCESS otherwise + */ +NCBI_XBLAST_EXPORT +int +_PSIConvertFreqRatiosToPSSM(_PSIInternalPssmData* internal_pssm, + const Uint1* query, + const BlastScoreBlk* sbp, + const double* std_probs); + +/** Scales the PSSM (stage 7) + * @param query query sequence in ncbistdaa encoding. The length of this + * sequence is read from internal_pssm->ncols [in] + * @param std_probs array containing the standard background residue + * probabilities [in] + * @param internal_pssm PSSM being computed [in|out] + * @param sbp score block structure initialized for the scoring system used + * with the query sequence [in|out] + * @return PSIERR_BADPARAM if arguments are NULL, PSIERR_POSITIVEAVGSCORE if + * the average score of the generated PSSM is positive, PSI_SUCCESS otherwise + */ +NCBI_XBLAST_EXPORT +int +_PSIScaleMatrix(const Uint1* query, + const double* std_probs, + _PSIInternalPssmData* internal_pssm, + BlastScoreBlk* sbp); + +/****************************************************************************/ +/* Function prototypes for auxiliary functions for the stages above */ + + +/** Updates the Karlin-Altschul parameters based on the query sequence and + * PSSM's score frequencies. Port of blastool.c's updateLambdaK + * @param pssm PSSM [in] + * @param query query sequence in ncbistdaa encoding [in] + * @param query_length length of the query sequence above [in] + * @param std_probs array containing the standard background residue + * probabilities [in] + * @param sbp Score block structure where the calculated lambda and K will be + * returned [in|out] + */ +NCBI_XBLAST_EXPORT +void +_PSIUpdateLambdaK(const int** pssm, + const Uint1* query, + Uint4 query_length, + const double* std_probs, + BlastScoreBlk* sbp); + +/** Provides a similar function to _PSIScaleMatrix but it performs the scaling + * as IMPALA did, i.e.: allowing the specification of a scaling factor and when + * calculating the score probabilities, the query length includes 'X' residues. + * @todo Ideally all scaling code should be refactored so that it is + * consolidated, eliminating the need for blast_posit.[hc]. Please note that + * blast_kappa.c's scalePosMatrix also does something very similar. + * @todo remove std_probs as it's not used + */ +NCBI_XBLAST_EXPORT +int +_IMPALAScaleMatrix(const Uint1* query, const double* std_probs, + _PSIInternalPssmData* internal_pssm, + BlastScoreBlk* sbp, + double scaling_factor); + +/** Marks the (start, stop] region corresponding to sequence seq_index in + * alignment so that it is not further considered for PSSM calculation. + * Note that the query sequence cannot be purged. + * @param msa multiple sequence alignment data [in|out] + * @param seq_index index of the sequence of interested in alignment [in] + * @param start start of the region to remove [in] + * @param stop stop of the region to remove [in] + * @return PSIERR_BADPARAM if no alignment is given, or if seq_index or stop + * are invalid, PSI_SUCCESS otherwise + */ +NCBI_XBLAST_EXPORT +int +_PSIPurgeAlignedRegion(_PSIPackedMsa* msa, + unsigned int seq_index, + unsigned int start, + unsigned int stop); + +/** Counts the number of sequences matching the query per query position + * (columns of the multiple alignment) as well as the number of residues + * present in each position of the query. + * Should be called after multiple alignment data has been purged from biased + * sequences. + * @param msa multiple sequence alignment structure [in|out] + */ +NCBI_XBLAST_EXPORT +void +_PSIUpdatePositionCounts(_PSIMsa* msa); + +/** Calculates the length of the sequence without including any 'X' residues. + * used in kappa.c + * @param seq sequence to examine [in] + * @param length length of the sequence above [in] + * @return number of non-X residues in the sequence + */ +NCBI_XBLAST_EXPORT +Uint4 +_PSISequenceLengthWithoutX(const Uint1* seq, Uint4 length); + +/** Compute the probabilities for each score in the PSSM. + * This is only valid for protein sequences. + * FIXME: Should this be moved to blast_stat.[hc]? + * used in kappa.c in notposfillSfp() + * @param pssm PSSM for which to compute the score probabilities [in] + * @param query query sequence for the PSSM above in ncbistdaa encoding [in] + * @param query_length length of the query sequence above [in] + * @param std_probs array containing the standard background residue + * probabilities [in] + * @param sbp score block structure initialized for the scoring system used + * with the query sequence [in] + * @return structure containing the score frequencies, or NULL in case of error + */ +NCBI_XBLAST_EXPORT +Blast_ScoreFreq* +_PSIComputeScoreProbabilities(const int** pssm, + const Uint1* query, + Uint4 query_length, + const double* std_probs, + const BlastScoreBlk* sbp); + +/** Collects diagnostic information from the process of creating the PSSM + * @param msa multiple sequence alignment data structure [in] + * @param aligned_block aligned regions' extents [in] + * @param seq_weights sequence weights data structure [in] + * @param internal_pssm structure containing PSSM's frequency ratios [in] + * @param diagnostics output parameter [out] + * @return PSI_SUCCESS on success, PSIERR_OUTOFMEM if memory allocation fails + * or PSIERR_BADPARAM if any of its arguments is NULL + */ +NCBI_XBLAST_EXPORT +int +_PSISaveDiagnostics(const _PSIMsa* msa, + const _PSIAlignedBlock* aligned_block, + const _PSISequenceWeights* seq_weights, + const _PSIInternalPssmData* internal_pssm, + PSIDiagnosticsResponse* diagnostics); + +/** Collects diagnostic information from the process of creating the CDD-based + * PSSM + * @param cd_msa multiple alignment of CDs data structure [in] + * @param seq_weights sequence weights data structure [in] + * @param internal_pssm structure containing PSSM's frequency ratios [in] + * @param diagnostics output parameter [out] + * @return PSI_SUCCESS on success, PSIERR_OUTOFMEM if memory allocation fails + * or PSIERR_BADPARAM if any of its arguments is NULL + */ +int +_PSISaveCDDiagnostics(const PSICdMsa* msa, + const _PSISequenceWeights* seq_weights, + const _PSIInternalPssmData* internal_pssm, + PSIDiagnosticsResponse* diagnostics); + + +/** Calculates the information content from the scoring matrix + * @param score_mat alphabet by alphabet_sz matrix of scores (const) [in] + * @param std_prob standard residue probabilities [in] + * @param query query sequence [in] + * @param query_length length of the query [in] + * @param alphabet_sz length of the alphabet used by the query [in] + * @param lambda lambda parameter [in] FIXME documentation + * @return array of length query_length containing the information content per + * query position or NULL on error (e.g.: out-of-memory or NULL parameters) + */ +NCBI_XBLAST_EXPORT +double* +_PSICalculateInformationContentFromScoreMatrix( + Int4** score_mat, + const double* std_prob, + const Uint1* query, + Uint4 query_length, + Uint4 alphabet_sz, + double lambda); + +/** Calculates the information content from the residue frequencies calculated + * in stage 5 of the PSSM creation algorithm + * Corresponds to posit.c:posFreqsToInformation + * @sa _PSIComputeFreqRatios: stage 5 + * @param freq_ratios matrix of frequency ratios (dimensions: query_length x + * alphabet_sz) (const) [in] + * @param std_prob standard residue probabilities [in] + * @param query_length length of the query [in] + * @param alphabet_sz length of the alphabet used by the query [in] + * @return array of length query_length containing the information content per + * query position or NULL on error (e.g.: out-of-memory or NULL parameters) + */ +NCBI_XBLAST_EXPORT +double* +_PSICalculateInformationContentFromFreqRatios( + double** freq_ratios, + const double* std_prob, + Uint4 query_length, + Uint4 alphabet_sz); + +#ifdef DEBUG_PSSM_ENGINE +void __printMsa(const char* filename, const _PSIPackedMsa* msa); +void __printMsaFP(FILE* fp, const _PSIPackedMsa* msa); +#endif /* DEBUG_PSSM_ENGINE */ + +/** Enable NCBI structure group customization to discard the query sequence, + * as this really isn't the result of a PSI-BLAST iteration, but rather an + * artificial consensus sequence of the multiple sequence alignment + * constructed by them. This should be called after _PSIPurgeBiasedSegments. + */ +NCBI_XBLAST_EXPORT +void +_PSIStructureGroupCustomization(_PSIMsa* msa); + +/** Structure group validation function for multiple sequence alignment + * structure. Should be called after _PSIStructureGroupCustomization. + * @param msa multiple sequence alignment data structure [in] + * @return One of the errors defined above if validation fails or bad + * parameter is passed in, else PSI_SUCCESS + */ +NCBI_XBLAST_EXPORT +int +_PSIValidateMSA_StructureGroup(const _PSIMsa* msa); + +/** Validation of multiple alignment of conserved domains structure + * @param cd_msa multiple alignment of CDs [in] + * @param alphabet_size alphabet size [in] + * @return One of the errors defined above if validation fails or bad + * parameter is passed in, else PSI_SUCCESS + */ +NCBI_XBLAST_EXPORT +int +_PSIValidateCdMSA(const PSICdMsa* cd_msa, Uint4 alphabet_size); + +#ifdef __cplusplus +} +#endif + +#endif /* !ALGO_BLAST_CORE__BLAST_PSI_PRIV__H */ diff --git a/src/lib/blast/blast_query_info.h b/src/lib/blast/blast_query_info.h index 58415f3ce..0e40fedd8 100644 --- a/src/lib/blast/blast_query_info.h +++ b/src/lib/blast/blast_query_info.h @@ -1,4 +1,4 @@ -/* $Id: blast_query_info.h 419604 2013-11-26 22:49:06Z rafanovi $ +/* $Id$ * =========================================================================== * * PUBLIC DOMAIN NOTICE @@ -37,37 +37,56 @@ #include "ncbi_std.h" #include "blast_def.h" #include "blast_program.h" +#include +#include #ifdef __cplusplus extern "C" { #endif +/** Information about paired segments (for mapping short reads) + */ +typedef enum EMagicBlastSegmentInfo { + eNoSegments = 0, /**< Sequence is not part of a pair */ + fFirstSegmentFlag = 1, /**< The first sequence of a pair */ + fLastSegmentFlag = 1 << 1, /**< The last sequence of a pair */ + fPartialFlag = 1 << 2, /**< The other segment is not present (did not + pass quality filtering */ + eFirstSegment = fFirstSegmentFlag, /**< The first sequence of a pair with + both sequences read and accepted */ + eLastSegment = fLastSegmentFlag /** The last sequence of a pair with + both sequences read and accepted */ +} EMagicBlastSegmentInfo; + /** The context related information */ typedef struct BlastContextInfo { - Int4 query_offset; /**< Offset of this query, strand or frame in the + int query_offset; /**< Offset of this query, strand or frame in the concatenated super-query. */ - Int4 query_length; /**< Length of this query, strand or frame */ - Int8 eff_searchsp; /**< Effective search space for this context. */ - Int4 length_adjustment; /**< Length adjustment for boundary conditions */ - Int4 query_index; /**< Index of query (same for all frames) */ - Int1 frame; /**< Frame number (-1, -2, -3, 0, 1, 2, or 3) */ + int query_length; /**< Length of this query, strand or frame */ + int eff_searchsp; /**< Effective search space for this context. */ + int length_adjustment; /**< Length adjustment for boundary conditions */ + int query_index; /**< Index of query (same for all frames) */ + int frame; /**< Frame number (-1, -2, -3, 0, 1, 2, or 3) */ bool is_valid; /**< Determine if this context is valid or not. This field should be set only by the setup code and read by subsequent stages of the BLAST search */ + int segment_flags; /**< Flags describing segments for paired reads */ } BlastContextInfo; /** Forward declaration of SPHIQueryInfo */ struct SPHIQueryInfo; -/** The query related information +/** The query related information */ typedef struct BlastQueryInfo { - Int4 first_context; /**< Index of the first element of the context array */ - Int4 last_context; /**< Index of the last element of the context array */ + int first_context; /**< Index of the first element of the context array */ + int last_context; /**< Index of the last element of the context array */ int num_queries; /**< Number of query sequences */ BlastContextInfo * contexts; /**< Information per context */ - Uint4 max_length; /**< Length of the longest among the concatenated + unsigned max_length; /**< Length of the longest among the concatenated + queries */ + int min_length; /**< Length of the shortest among the concatenated queries */ struct SPHIQueryInfo* pattern_info; /**< Counts of PHI BLAST pattern occurrences, used in PHI BLAST only. */ @@ -92,7 +111,7 @@ BlastQueryInfo* BlastQueryInfoDup(const BlastQueryInfo* query_info); * @return Query index in a set of queries or -1 on error */ NCBI_XBLAST_EXPORT -Int4 Blast_GetQueryIndexFromContext(Int4 context, EBlastProgramType program); +int Blast_GetQueryIndexFromContext(int context, EBlastProgramType program); /** Return the query index (zero based), given the query offset @@ -103,26 +122,26 @@ Int4 Blast_GetQueryIndexFromContext(Int4 context, EBlastProgramType program); * @return Query Index in a set of queries */ NCBI_XBLAST_EXPORT -Int4 Blast_GetQueryIndexFromQueryOffset(Int4 query_offset, EBlastProgramType program, const BlastQueryInfo* query_info); +int Blast_GetQueryIndexFromQueryOffset(int query_offset, EBlastProgramType program, const BlastQueryInfo* query_info); /** Retrieve a query sequence's search space * @param qinfo BlastQueryInfo structure [in] * @param program CORE program type [in] - * @param query_index number of the query + * @param query_index number of the query * (query_index < BlastQueryInfo::num_queries) [in] * @return the search space of the query sequence requested or 0 if this is not * set */ NCBI_XBLAST_EXPORT -Int8 +unsigned BlastQueryInfoGetEffSearchSpace(const BlastQueryInfo* qinfo, EBlastProgramType program, - Int4 query_index); + int query_index); /** Set a query sequence's search space * @param qinfo BlastQueryInfo structure [in] * @param program CORE program type [in] - * @param query_index number of the query + * @param query_index number of the query * (query_index < BlastQueryInfo::num_queries) [in] * @param eff_searchsp the effective search space to use [in] */ @@ -130,50 +149,50 @@ NCBI_XBLAST_EXPORT void BlastQueryInfoSetEffSearchSpace(BlastQueryInfo* qinfo, EBlastProgramType program, - Int4 query_index, - Int8 eff_searchsp); + int query_index, + int eff_searchsp); /** Obtains the sequence length for a given query in the query, without taking - * into consideration any applicable translations + * into consideration any applicable translations * @param qinfo BlastQueryInfo structure [in] * @param program CORE program type [in] - * @param query_index number of the query + * @param query_index number of the query * (query_index < BlastQueryInfo::num_queries) [in] * @return the length of the query sequence requested */ NCBI_XBLAST_EXPORT -Int4 BlastQueryInfoGetQueryLength(const BlastQueryInfo* qinfo, +int BlastQueryInfoGetQueryLength(const BlastQueryInfo* qinfo, EBlastProgramType program, - Int4 query_index); + int query_index); /** Create auxiliary query structures with all data corresponding - * to a single query sequence within a concatenated set. Allocates the + * to a single query sequence within a concatenated set. Allocates the * structures if the pointers are NULL on input; otherwise only changes the * contents. - * @param one_query_info_ptr Pointer to the query information structure for a + * @param one_query_info_ptr Pointer to the query information structure for a * single query. Allocated and filled here, so the * caller of this function will be responsible for * freeing it. [out] * @param one_query_ptr Pointer to the query sequence block structure; allocated - * here, but the contents are not allocated; it is still + * here, but the contents are not allocated; it is still * safe to free by the caller after use. [out] - * @param query_info Query information structure containing information about a + * @param query_info Query information structure containing information about a * concatenated set. [in] - * @param query Query sequence block corresponding to a concatenated set of + * @param query Query sequence block corresponding to a concatenated set of * queries. [in] - * @param query_index Which query index to create the auxiliary structures + * @param query_index Which query index to create the auxiliary structures * for? [in] * @return -1 if memory allocation failed; 0 on success */ NCBI_XBLAST_EXPORT -Int2 Blast_GetOneQueryStructs(BlastQueryInfo** one_query_info_ptr, +int Blast_GetOneQueryStructs(BlastQueryInfo** one_query_info_ptr, BLAST_SequenceBlk** one_query_ptr, - const BlastQueryInfo* query_info, - BLAST_SequenceBlk* query, Int4 query_index); + const BlastQueryInfo* query_info, + BLAST_SequenceBlk* query, int query_index); /** Search BlastContextInfo structures for the specified offset */ NCBI_XBLAST_EXPORT -Int4 BSearchContextInfo(Int4 n, const BlastQueryInfo * A); +int BSearchContextInfo(int n, const BlastQueryInfo * A); /** Get the number of bytes required for the concatenated sequence * buffer, given a query info structure. The context data should @@ -182,19 +201,19 @@ Int4 BSearchContextInfo(Int4 n, const BlastQueryInfo * A); * @return Number of bytes for all queries and inter-query marks. */ NCBI_XBLAST_EXPORT -Uint4 +int QueryInfo_GetSeqBufLen(const BlastQueryInfo* qinfo); -/** Copy the context query offsets to an allocated array of Int4. +/** Copy the context query offsets to an allocated array of int. * @param info Describes the concatenated query. * @return Allocated array. */ NCBI_XBLAST_EXPORT -Int4 * ContextOffsetsToOffsetArray(const BlastQueryInfo* info); +int * ContextOffsetsToOffsetArray(const BlastQueryInfo* info); -/** Copy the context query offsets from an array of Int4, allocating +/** Copy the context query offsets from an array of int, allocating * the context array if needed. * @param info Destination for the values. * @param new_offsets Array of values to copy from. @@ -202,7 +221,7 @@ Int4 * ContextOffsetsToOffsetArray(const BlastQueryInfo* info); */ NCBI_XBLAST_EXPORT void OffsetArrayToContextOffsets(BlastQueryInfo * info, - Int4 * new_offsets, + int * new_offsets, EBlastProgramType prog); #ifdef __cplusplus @@ -210,3 +229,4 @@ void OffsetArrayToContextOffsets(BlastQueryInfo * info, #endif #endif /* !ALGO_BLAST_CORE__BLAST_QUERY_INFO__H */ + diff --git a/src/lib/blast/blast_setup.h b/src/lib/blast/blast_setup.h new file mode 100644 index 000000000..4ac7ac339 --- /dev/null +++ b/src/lib/blast/blast_setup.h @@ -0,0 +1,141 @@ +/* $Id$ + * =========================================================================== + * + * PUBLIC DOMAIN NOTICE + * National Center for Biotechnology Information + * + * This software/database is a "United States Government Work" under the + * terms of the United States Copyright Act. It was written as part of + * the author's official duties as a United States Government employee and + * thus cannot be copyrighted. This software/database is freely available + * to the public for use. The National Library of Medicine and the U.S. + * Government have not placed any restriction on its use or reproduction. + * + * Although all reasonable efforts have been taken to ensure the accuracy + * and reliability of the software and data, the NLM and the U.S. + * Government do not and cannot warrant the performance or results that + * may be obtained by using this software or data. The NLM and the U.S. + * Government disclaim all warranties, express or implied, including + * warranties of performance, merchantability or fitness for any particular + * purpose. + * + * Please cite the author in any work or product based on this material. + * + * =========================================================================== + * + * Author: Tom Madden + * + */ + +/** @file blast_setup.h + * Utilities initialize/setup BLAST. + */ + +#ifndef __BLAST_SETUP__ +#define __BLAST_SETUP__ + +#include "ncbi_std.h" +#include "blast_export.h" +#include "blast_def.h" +#include "blast_query_info.h" +#include "blast_options.h" + +#include "blast_message.h" +#include "blast_stat.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/** "Main" setup routine for BLAST. Calculates all information for BLAST search + * that is dependent on the ASN.1 structures. + * @todo FIXME: this function only filters query and sets up score block structure + * @param program_number Type of BLAST program (0=blastn, ...). [in] + * @param qsup_options options for query setup. [in] + * @param scoring_options options for scoring. [in] + * @param query_blk BLAST_SequenceBlk* for the query. [in] + * @param query_info The query information block [in] + * @param scale_factor Multiplier for cutoff and dropoff scores [in] + * @param lookup_segments Start/stop locations for non-masked query + * segments [out] + * @param mask masking locations. [out] + * @param sbpp Contains scoring information. [out] + * @param blast_message error or warning [out] + * @param get_path callback function to get matrix path [in] + */ +NCBI_XBLAST_EXPORT +Int2 BLAST_MainSetUp(EBlastProgramType program_number, + const QuerySetUpOptions* qsup_options, + const BlastScoringOptions* scoring_options, + BLAST_SequenceBlk* query_blk, + const BlastQueryInfo* query_info, + double scale_factor, + BlastSeqLoc* *lookup_segments, + BlastMaskLoc* *mask, + BlastScoreBlk* *sbpp, + Blast_Message* *blast_message, + GET_MATRIX_PATH get_path); + +/** Blast_ScoreBlkKbpGappedCalc, fills the ScoreBlkPtr for a gapped search. + * Should be moved to blast_stat.c in the future. + * @param sbp Contains fields to be set, should not be NULL. [out] + * @param scoring_options Scoring_options [in] + * @param program Used to set fields on sbp [in] + * @param query_info Query information containing context information [in] + * @param error_return Pointer to structure for returning errors. [in][out] + * @return Status. + */ +NCBI_XBLAST_EXPORT +Int2 Blast_ScoreBlkKbpGappedCalc(BlastScoreBlk * sbp, + const BlastScoringOptions * scoring_options, + EBlastProgramType program, + const BlastQueryInfo * query_info, + Blast_Message** error_return); + + + +NCBI_XBLAST_EXPORT +Int2 Blast_ScoreBlkMatrixInit(EBlastProgramType program_number, + const BlastScoringOptions* scoring_options, + BlastScoreBlk* sbp, + GET_MATRIX_PATH get_path); + +/** Initializes the score block structure. + * @param query_blk Query sequence(s) [in] + * @param query_info Additional query information [in] + * @param scoring_options Scoring options [in] + * @param program_number BLAST program type [in] + * @param sbpp Initialized score block [out] + * @param scale_factor Matrix scaling factor for this search [in] + * @param blast_message Error message [out] + * @param get_path callback function to get matrix path [in] + */ +NCBI_XBLAST_EXPORT +Int2 BlastSetup_ScoreBlkInit(BLAST_SequenceBlk* query_blk, + const BlastQueryInfo* query_info, + const BlastScoringOptions* scoring_options, + EBlastProgramType program_number, + BlastScoreBlk* *sbpp, + double scale_factor, + Blast_Message* *blast_message, + GET_MATRIX_PATH get_path); + + +/** Adjusts the mask locations coordinates to a sequence interval. Removes those + * mask locations that do not intersect the interval. Can do this either for all + * queries or only for the first one. + * @param mask Structure containing a mask location. [in] [out] + * @param from Starting offset of a sequence interval [in] + * @param to Ending offset of a sequence interval [in] + */ +NCBI_XBLAST_EXPORT +void +BlastSeqLoc_RestrictToInterval(BlastSeqLoc* *mask, Int4 from, Int4 to); + + + +#ifdef __cplusplus +} +#endif +#endif /* !__BLAST_SETUP__ */ diff --git a/src/lib/blast/blast_stat.cpp b/src/lib/blast/blast_stat.cpp new file mode 100644 index 000000000..0639b0332 --- /dev/null +++ b/src/lib/blast/blast_stat.cpp @@ -0,0 +1,3 @@ +// +// Created by dimi on 02.05.22. +// diff --git a/src/lib/blast/blast_stat.h b/src/lib/blast/blast_stat.h index 74b65d9d4..f9c035099 100644 --- a/src/lib/blast/blast_stat.h +++ b/src/lib/blast/blast_stat.h @@ -62,12 +62,12 @@ typedef char* (*GET_MATRIX_PATH) (const char*, bool); Structure to hold the Karlin-Altschul parameters. */ typedef struct Blast_KarlinBlk { - double Lambda; /**< Lambda value used in statistics */ - double K; /**< K value used in statistics */ - double logK; /**< natural log of K value used in statistics */ - double H; /**< H value used in statistics */ - double paramC; /**< for use in seed. */ - } Blast_KarlinBlk; + double Lambda; /**< Lambda value used in statistics */ + double K; /**< K value used in statistics */ + double logK; /**< natural log of K value used in statistics */ + double H; /**< H value used in statistics */ + double paramC; /**< for use in seed. */ +} Blast_KarlinBlk; /** @@ -78,42 +78,40 @@ typedef struct Blast_KarlinBlk { The erfc(x) is tabulated in p with step h, starting from a to b */ typedef struct erfc_table { - double eps; - double a; - double b; - Int4 N; - double h; - double *p; - } erfc_table; + double eps; + double a; + double b; + Int4 N; + double h; + double *p; +} erfc_table; /** Structure to hold the Gumbel parameters (for FSC). */ typedef struct Blast_GumbelBlk { - double Lambda; /**< the unscaled Lambda value */ - double C; - double G; /**< G is the total penalty for extension */ - double a; /**< avg(L) = a y + b */ - double Alpha; /**< var(L) = alpha y + beta */ - double Sigma; /**< cov(L) = sigma y + tau */ - double a_un; /**< Ungapped a */ - double Alpha_un; /**< Ungapped alpha */ + double Lambda; /**< the unscaled Lambda value */ + double C; + double G; /**< G is the total penalty for extension */ + double a; /**< avg(L) = a y + b */ + double Alpha; /**< var(L) = alpha y + beta */ + double Sigma; /**< cov(L) = sigma y + tau */ + double a_un; /**< Ungapped a */ + double Alpha_un; /**< Ungapped alpha */ - double b; /**< 2*G*(a_un - a) */ - double Beta; /**< 2*G*(alpha_un - alpha) */ - double Tau; /**< 2*G*(alpha_un - Sigma) */ + double b; /**< 2*G*(a_un - a) */ + double Beta; /**< 2*G*(alpha_un - alpha) */ + double Tau; /**< 2*G*(alpha_un - Sigma) */ - Int8 db_length; /**< total length of database */ + Int8 db_length; /**< total length of database */ - bool filled; /**< flag indicate the values of gbp are prepared */ - - } Blast_GumbelBlk; + bool filled; /**< flag indicate the values of gbp are prepared */ +} Blast_GumbelBlk; -/******************************************************************** +/******************************************************************** Structures relating to scoring or the BlastScoreBlk - ********************************************************************/ #define BLAST_SCORE_MIN INT2_MIN /**< minimum allowed score (for one letter comparison). */ @@ -173,50 +171,50 @@ SPsiBlastScoreMatrixFree(SPsiBlastScoreMatrix* matrix); /** Structure used for scoring calculations. */ typedef struct BlastScoreBlk { - bool protein_alphabet; /**< TRUE if alphabet_code is for a + bool protein_alphabet; /**< TRUE if alphabet_code is for a protein alphabet (e.g., ncbistdaa etc.), FALSE for nt. alphabets. */ - Uint1 alphabet_code; /**< NCBI alphabet code. */ - Int2 alphabet_size; /**< size of alphabet. */ - Int2 alphabet_start; /**< numerical value of 1st letter. */ - char* name; /**< name of scoring matrix. */ - ListNode* comments; /**< Comments about scoring matrix. */ - SBlastScoreMatrix* matrix; /**< scoring matrix data */ - SPsiBlastScoreMatrix* psi_matrix; /**< PSSM and associated data. If this + Uint1 alphabet_code; /**< NCBI alphabet code. */ + Int2 alphabet_size; /**< size of alphabet. */ + Int2 alphabet_start; /**< numerical value of 1st letter. */ + char* name; /**< name of scoring matrix. */ + ListNode* comments; /**< Comments about scoring matrix. */ + SBlastScoreMatrix* matrix; /**< scoring matrix data */ + SPsiBlastScoreMatrix* psi_matrix; /**< PSSM and associated data. If this is not NULL, then the BLAST search is position specific (i.e.: PSI-BLAST) */ - bool matrix_only_scoring; /**< Score ungapped/gapped alignment only + bool matrix_only_scoring; /**< Score ungapped/gapped alignment only using the matrix parameters and - with raw scores. Ignore - penalty/reward and do not report + with raw scores. Ignore + penalty/reward and do not report Karlin-Altschul stats. This is used by the rmblastn program. -RMH- */ - bool complexity_adjusted_scoring; /**< Use cross_match-like complexity + bool complexity_adjusted_scoring; /**< Use cross_match-like complexity adjustment on raw scores. -RMH- */ - Int4 loscore; /**< Min. substitution scores */ - Int4 hiscore; /**< Max. substitution scores */ - Int4 penalty; /**< penalty for mismatch in blastn. */ - Int4 reward; /**< reward for match in blastn. */ - double scale_factor; /**< multiplier for all cutoff and dropoff scores */ - bool read_in_matrix; /**< If TRUE, matrix is read in, otherwise + Int4 loscore; /**< Min. substitution scores */ + Int4 hiscore; /**< Max. substitution scores */ + Int4 penalty; /**< penalty for mismatch in blastn. */ + Int4 reward; /**< reward for match in blastn. */ + double scale_factor; /**< multiplier for all cutoff and dropoff scores */ + bool read_in_matrix; /**< If TRUE, matrix is read in, otherwise produce one from penalty and reward above. @todo should this be an allowed way of specifying the matrix to use? */ - Blast_ScoreFreq** sfp; /**< score frequencies for scoring matrix. */ - /* kbp & kbp_gap are ptrs that should be set to kbp_std, kbp_psi, etc. */ - Blast_KarlinBlk** kbp; /**< Karlin-Altschul parameters. Actually just a placeholder. */ - Blast_KarlinBlk** kbp_gap; /**< K-A parameters for gapped alignments. Actually just a placeholder. */ - Blast_GumbelBlk* gbp; /**< Gumbel parameters for FSC. */ - /* Below are the Karlin-Altschul parameters for non-position based ('std') - and position based ('psi') searches. */ - Blast_KarlinBlk **kbp_std, /**< K-A parameters for ungapped alignments */ - **kbp_psi, /**< K-A parameters for position-based alignments. */ - **kbp_gap_std, /**< K-A parameters for std (not position-based) alignments */ - **kbp_gap_psi; /**< K-A parameters for psi alignments. */ - Blast_KarlinBlk* kbp_ideal; /**< Ideal values (for query with average database composition). */ - Int4 number_of_contexts; /**< Used by sfp and kbp, how large are these*/ - Uint1* ambiguous_res; /**< Array of ambiguous res. (e.g, 'X', 'N')*/ - Int2 ambig_size, /**< size of array above. FIXME: not needed here? */ - ambig_occupy; /**< How many occupied? */ - bool round_down; /**< Score must be rounded down to nearest even score if odd. */ + Blast_ScoreFreq** sfp; /**< score frequencies for scoring matrix. */ + /* kbp & kbp_gap are ptrs that should be set to kbp_std, kbp_psi, etc. */ + Blast_KarlinBlk** kbp; /**< Karlin-Altschul parameters. Actually just a placeholder. */ + Blast_KarlinBlk** kbp_gap; /**< K-A parameters for gapped alignments. Actually just a placeholder. */ + Blast_GumbelBlk* gbp; /**< Gumbel parameters for FSC. */ + /* Below are the Karlin-Altschul parameters for non-position based ('std') + and position based ('psi') searches. */ + Blast_KarlinBlk **kbp_std, /**< K-A parameters for ungapped alignments */ + **kbp_psi, /**< K-A parameters for position-based alignments. */ + **kbp_gap_std, /**< K-A parameters for std (not position-based) alignments */ + **kbp_gap_psi; /**< K-A parameters for psi alignments. */ + Blast_KarlinBlk* kbp_ideal; /**< Ideal values (for query with average database composition). */ + Int4 number_of_contexts; /**< Used by sfp and kbp, how large are these*/ + Uint1* ambiguous_res; /**< Array of ambiguous res. (e.g, 'X', 'N')*/ + Int2 ambig_size, /**< size of array above. FIXME: not needed here? */ + ambig_occupy; /**< How many occupied? */ + bool round_down; /**< Score must be rounded down to nearest even score if odd. */ } BlastScoreBlk; /** Scoring matrix data used for compressed protein alphabets */ @@ -236,7 +234,7 @@ typedef struct SCompressedAlphabet { NCBI_XBLAST_EXPORT SCompressedAlphabet* SCompressedAlphabetNew(BlastScoreBlk *sbp, - Int4 compressed_alphabet_size, + Int4 compressed_alphabet_size, double scale_factor); /** Free a compressed alphabet and score matrix @@ -247,7 +245,7 @@ NCBI_XBLAST_EXPORT SCompressedAlphabet* SCompressedAlphabetFree(SCompressedAlphabet *alphabet); -/** +/** Stores the letter frequency of a sequence or database. */ typedef struct Blast_ResFreq { @@ -256,7 +254,7 @@ typedef struct Blast_ResFreq { double* prob0; /**< probs, zero offset. */ } Blast_ResFreq; -/** +/** * Check that score blk is valid, returns zero if it is. * @param sbp ScoreBlk to check [in] * @return zero if valid @@ -299,12 +297,12 @@ Int2 BLAST_ScoreSetAmbigRes (BlastScoreBlk* sbp, char ambiguous_res); * @param query Buffer containing (concatenated) query sequence [in] * @param query_info Information about offsets of concatenated queries [in] * @param blast_message returns queries that could not be processed [out] - * @return 0 if ungapped Karlin-Altschul parameters could be calculated for - * all of the query sequence's contexts; 1 if any of the contexts + * @return 0 if ungapped Karlin-Altschul parameters could be calculated for + * all of the query sequence's contexts; 1 if any of the contexts * failed (but all others will be populated). */ -/** This function fills in the BlastScoreBlk structure. +/** This function fills in the BlastScoreBlk structure. * Tasks are: * -read in the matrix * -set maxscore @@ -314,7 +312,7 @@ Int2 BLAST_ScoreSetAmbigRes (BlastScoreBlk* sbp, char ambiguous_res); */ NCBI_XBLAST_EXPORT Int2 Blast_ScoreBlkMatrixFill (BlastScoreBlk* sbp, GET_MATRIX_PATH get_path); - + /** Callocs a Blast_KarlinBlk * @return pointer to the Blast_KarlinBlk */ @@ -326,7 +324,7 @@ Blast_KarlinBlk* Blast_KarlinBlkNew (void); * @param kbp_to Karlin block to copy values to [in] [out] * @param kbp_from Karlin block to copy values from [in] * @return 0 on success; -1 if either argument is NULL on input. - */ + */ NCBI_XBLAST_EXPORT Int2 Blast_KarlinBlkCopy(Blast_KarlinBlk* kbp_to, Blast_KarlinBlk* kbp_from); @@ -338,7 +336,7 @@ Int2 Blast_KarlinBlkCopy(Blast_KarlinBlk* kbp_to, Blast_KarlinBlk* kbp_from); NCBI_XBLAST_EXPORT Blast_KarlinBlk* Blast_KarlinBlkFree(Blast_KarlinBlk* kbp); -/** Fills in lambda, H, and K values, as calculated by Stephen Altschul +/** Fills in lambda, H, and K values, as calculated by Stephen Altschul * in Methods in Enzy. (vol 266, page 474). * @param kbp object to be filled in [in|out] * @param gap_open cost of gap existence [in] @@ -348,13 +346,13 @@ Blast_KarlinBlk* Blast_KarlinBlkFree(Blast_KarlinBlk* kbp); * @return zero on success */ NCBI_XBLAST_EXPORT -Int2 Blast_KarlinBlkGappedCalc (Blast_KarlinBlk* kbp, Int4 gap_open, - Int4 gap_extend, const char* matrix_name, Blast_Message** error_return); +Int2 Blast_KarlinBlkGappedCalc (Blast_KarlinBlk* kbp, Int4 gap_open, + Int4 gap_extend, const char* matrix_name, Blast_Message** error_return); /** Retrieves Karlin-Altschul parameters from precomputed tables, given the - * substitution and gap scores. Gap cost values greater than any of those + * substitution and gap scores. Gap cost values greater than any of those * listed in the tables ("greater" meaning that both values are greater than or - * equal, and at least one is strictly greater), are treated as infinite, and + * equal, and at least one is strictly greater), are treated as infinite, and * parameters values are copied from the ungapped Karlin block. * @param kbp Allocated Karlin block to fill [in] [out] * @param gap_open Gap openening (existence) cost [in] @@ -368,7 +366,7 @@ Int2 Blast_KarlinBlkGappedCalc (Blast_KarlinBlk* kbp, Int4 gap_open, */ NCBI_XBLAST_EXPORT Int2 -Blast_KarlinBlkNuclGappedCalc(Blast_KarlinBlk* kbp, Int4 gap_open, +Blast_KarlinBlkNuclGappedCalc(Blast_KarlinBlk* kbp, Int4 gap_open, Int4 gap_extend, Int4 reward, Int4 penalty, Blast_KarlinBlk* kbp_ungap, bool* round_down, @@ -378,7 +376,7 @@ Blast_KarlinBlkNuclGappedCalc(Blast_KarlinBlk* kbp, Int4 gap_open, /** Calculates the Karlin-Altschul parameters assuming standard residue * compositions for the query and subject sequences. It populates the kbp_ideal * field of its sbp argument. This is used if the query is translated and the - * calculated (real) Karlin parameters are bad, as they're calculated for + * calculated (real) Karlin parameters are bad, as they're calculated for * non-coding regions. * @param sbp ScoreBlk used to calculate "ideal" values. [in|out] * @return 0 on success, 1 on failure @@ -408,8 +406,8 @@ Int2 Blast_KarlinBlkGappedLoadFromTables(Blast_KarlinBlk* kbp, Int4 gap_open, In * @return zero on success */ NCBI_XBLAST_EXPORT -Int2 Blast_GumbelBlkCalc (Blast_GumbelBlk* gbp, Int4 gap_open, - Int4 gap_extend, const char* matrix_name, Blast_Message** error_return); +Int2 Blast_GumbelBlkCalc (Blast_GumbelBlk* gbp, Int4 gap_open, + Int4 gap_extend, const char* matrix_name, Blast_Message** error_return); /** Attempts to fill GumbelBlk for given gap opening, extensions etc. * @@ -422,18 +420,18 @@ Int2 Blast_GumbelBlkCalc (Blast_GumbelBlk* gbp, Int4 gap_open, * 2 if matrix found, but open, extend etc. values not supported. */ NCBI_XBLAST_EXPORT -Int2 Blast_GumbelBlkLoadFromTables(Blast_GumbelBlk* gbp, Int4 gap_open, - Int4 gap_extend, const char* matrix_name); +Int2 Blast_GumbelBlkLoadFromTables(Blast_GumbelBlk* gbp, Int4 gap_open, + Int4 gap_extend, const char* matrix_name); -/** Prints a messages about the allowed matrices, BlastKarlinBlkGappedFill should return 1 before this is called. +/** Prints a messages about the allowed matrices, BlastKarlinBlkGappedFill should return 1 before this is called. * @param matrix the matrix to print a message about [in] * @return the message */ NCBI_XBLAST_EXPORT char* BLAST_PrintMatrixMessage(const char *matrix); -/** Prints a messages about the allowed open etc values for the given matrix, - * BlastKarlinBlkGappedFill should return 2 before this is called. +/** Prints a messages about the allowed open etc values for the given matrix, + * BlastKarlinBlkGappedFill should return 2 before this is called. * @param matrix name of the matrix [in] * @param gap_open gap existence cost [in] * @param gap_extend cost to extend a gap by one [in] @@ -447,7 +445,7 @@ NCBI_XBLAST_EXPORT double Blast_KarlinLambdaNR(Blast_ScoreFreq* sfp, double initialLambdaGuess); -/** Calculates the Expect value based upon the search space and some Karlin-Altschul +/** Calculates the Expect value based upon the search space and some Karlin-Altschul * parameters. It is "simple" as it does not use sum-statistics. * @param S the score of the alignment. [in] * @param kbp the Karlin-Altschul parameters. [in] @@ -484,20 +482,20 @@ Int4 BLAST_SpougeEtoS (double E, Blast_KarlinBlk* kbp, Blast_GumbelBlk* gbp, Int * If given a database P-value, this routine will return a database * E-value; if given a pairwise P-value, it will return a pairwise * E-value. - * + * * In the context of a database search, the available P-value is often * a pairwise P-value, whereas the desired E-value is a database * E-value. When this it the case, the value returned by this routine * should be multiplied by the effective length of the database and * divided by the effective length of the subject. - * + * * @param p the P-value to be converted [in] @return the corresponding * expect value. */ NCBI_XBLAST_EXPORT double BLAST_KarlinPtoE(double p); /** Convert an E-value to a P-value. - * + * * E-values and P-values may either represent statistics of a database * search or represent statistics on the two sequences being compared. * If given a database E-value, this routine will return a database @@ -545,7 +543,7 @@ double BLAST_GapDecayDivisor(double decayrate, unsigned nsegs ); * @param gap_decay_rate Gap decay rate to use, if dodecay is set [in] */ NCBI_XBLAST_EXPORT -Int2 BLAST_Cutoffs (Int4 *S, double* E, Blast_KarlinBlk* kbp, +Int2 BLAST_Cutoffs (Int4 *S, double* E, Blast_KarlinBlk* kbp, Int8 searchsp, bool dodecay, double gap_decay_rate); /** Calculates the e-value for alignments with "small" gaps (typically @@ -559,9 +557,9 @@ Int2 BLAST_Cutoffs (Int4 *S, double* E, Blast_KarlinBlk* kbp, * @param subject_length effective len of the subject seq [in] * @param searchsp_eff effective size of the search space [in] * @param weight_divisor a divisor used to weight the e-value - * when multiple collections of alignments are being considered by + * when multiple collections of alignments are being considered by * the calling routine [in] - * @return the expect value + * @return the expect value */ NCBI_XBLAST_EXPORT double BLAST_SmallGapSumE (Int4 start_points, Int2 num, double xsum, @@ -616,16 +614,16 @@ double BLAST_LargeGapSumE (Int2 num, double xsum, Int8 searchsp_eff, double weight_divisor ); /** Extract the recommended gap existence and extension values. - * Only to be used with protein matrices. + * Only to be used with protein matrices. * @param matrixName name of the matrix [in] * @param gap_existence returns recommended existence cost [in|out] * @param gap_extension returns recommended extension cost [in|out] - * @return zero on success + * @return zero on success */ NCBI_XBLAST_EXPORT Int2 BLAST_GetProteinGapExistenceExtendParams(const char* matrixName, - Int4* gap_existence, - Int4* gap_extension); + Int4* gap_existence, + Int4* gap_extension); /** Extract the recommended gap existence and extension values. * Only to be used with blastn searches. @@ -633,19 +631,19 @@ Int2 BLAST_GetProteinGapExistenceExtendParams(const char* matrixName, * @param penalty mismatch score [in] * @param gap_existence returns recommended existence cost [in|out] * @param gap_extension returns recommended extension cost [in|out] - * @return zero on success + * @return zero on success */ NCBI_XBLAST_EXPORT Int2 BLAST_GetNucleotideGapExistenceExtendParams(Int4 reward, - Int4 penalty, - Int4* gap_existence, - Int4* gap_extension); + Int4 penalty, + Int4* gap_existence, + Int4* gap_extension); /** Check the validity of the reward and penalty scores. * Only to be used with blastn searches. * @param reward match score [in] * @param penalty mismatch score [in] - * @return TRUE on success + * @return TRUE on success */ NCBI_XBLAST_EXPORT bool BLAST_CheckRewardPenaltyScores(Int4 reward, Int4 penalty); @@ -662,10 +660,10 @@ bool BLAST_CheckRewardPenaltyScores(Int4 reward, Int4 penalty); */ NCBI_XBLAST_EXPORT void BLAST_GetAlphaBeta (const char* matrixName, double *alpha, - double *beta, bool gapped, Int4 gap_open, + double *beta, bool gapped, Int4 gap_open, Int4 gap_extend, const Blast_KarlinBlk* kbp_ungapped); -/** Extract the alpha and beta settings for these substitution and gap scores. +/** Extract the alpha and beta settings for these substitution and gap scores. * If substitution or gap costs are not found in the tables, assume an ungapped * search. Then alpha is computed using the formula Alpha = Lambda/H, and beta * is equal to 0 except for some special cases. @@ -673,25 +671,25 @@ void BLAST_GetAlphaBeta (const char* matrixName, double *alpha, * @param penalty Mismatch penalty score [in] * @param gap_open Gap opening (existence) cost [in] * @param gap_extend Gap extension cost [in] - * @param kbp Karlin block containing already computed Lambda, K and H + * @param kbp Karlin block containing already computed Lambda, K and H * parameters. * @param gapped_calculation Is this a gapped search? [in] * @param alpha Alpha parameter for this scoring system [out] * @param beta Beta parameter for this scoring system [out] */ NCBI_XBLAST_EXPORT -Int2 Blast_GetNuclAlphaBeta(Int4 reward, Int4 penalty, Int4 gap_open, +Int2 Blast_GetNuclAlphaBeta(Int4 reward, Int4 penalty, Int4 gap_open, Int4 gap_extend, Blast_KarlinBlk* kbp, bool gapped_calculation, double *alpha, double *beta); /** Rescale the PSSM, using composition-based statistics, for use * with RPS BLAST. This function produces a PSSM for a single RPS DB - * sequence (of size db_seq_length) and incorporates information from + * sequence (of size db_seq_length) and incorporates information from * the RPS blast query. Each individual database sequence must call this * function to retrieve its own PSSM. The matrix is returned (and must - * be freed elsewhere). posMatrix is the portion of the complete - * concatenated PSSM that is specific to this DB sequence + * be freed elsewhere). posMatrix is the portion of the complete + * concatenated PSSM that is specific to this DB sequence * @todo revise to use existing code * @param scalingFactor used to rescale Lambda [in] * @param rps_query_length length of query sequence [in] @@ -699,41 +697,41 @@ Int2 Blast_GetNuclAlphaBeta(Int4 reward, Int4 penalty, Int4 gap_open, * @param db_seq_length Length of the database sequence [in] * @param posMatrix matrix (actual) values to be used [in] * @param sbp Structure with score matrix parameters [in][out] - * @return rescaled pssm + * @return rescaled pssm */ NCBI_XBLAST_EXPORT -Int4 ** RPSRescalePssm(double scalingFactor, Int4 rps_query_length, - const Uint1 * rps_query_seq, Int4 db_seq_length, - Int4 **posMatrix, BlastScoreBlk *sbp); +Int4 ** RPSRescalePssm(double scalingFactor, Int4 rps_query_length, + const Uint1 * rps_query_seq, Int4 db_seq_length, + Int4 **posMatrix, BlastScoreBlk *sbp); -/** +/** * Computes the adjustment to the lengths of the query and database sequences - * that is used to compensate for edge effects when computing evalues. + * that is used to compensate for edge effects when computing evalues. * * The length adjustment is an integer-valued approximation to the fixed * point of the function * - * f(ell) = beta + + * f(ell) = beta + * (alpha/lambda) * (log K + log((m - ell)*(n - N ell))) * * where m is the query length n is the length of the database and N is the * number of sequences in the database. The values beta, alpha, lambda and * K are statistical, Karlin-Altschul parameters. - * - * The value of the length adjustment computed by this routine, A, + * + * The value of the length adjustment computed by this routine, A, * will always be an integer smaller than the fixed point of * f(ell). Usually, it will be the largest such integer. However, the - * computed length adjustment, A, will also be so small that + * computed length adjustment, A, will also be so small that * * K * (m - A) * (n - N * A) > MAX(m,n). * * Moreover, an iterative method is used to compute A, and under - * unusual circumstances the iterative method may not converge. + * unusual circumstances the iterative method may not converge. * * @param K the statistical parameter K [in] * @param logK the natural logarithm of K [in] - * @param alpha_d_lambda the ratio of the statistical parameters + * @param alpha_d_lambda the ratio of the statistical parameters * alpha and lambda (for ungapped alignments, the * value 1/H should be used) [in] * @param beta the statistical parameter beta (for ungapped @@ -790,7 +788,7 @@ NCBI_XBLAST_EXPORT Blast_ScoreFreq* Blast_ScoreFreqNew(Int4 score_min, Int4 score_max); -/** Deallocates the score frequencies structure +/** Deallocates the score frequencies structure * @param sfp the structure to deallocate [in] * @return NULL */ @@ -798,7 +796,7 @@ NCBI_XBLAST_EXPORT Blast_ScoreFreq* Blast_ScoreFreqFree(Blast_ScoreFreq* sfp); -/** Fills a buffer with the 'standard' alphabet +/** Fills a buffer with the 'standard' alphabet * (given by STD_AMINO_ACID_FREQS[index].ch). * * @param alphabet_code specifies alphabet [in] @@ -808,7 +806,7 @@ Blast_ScoreFreqFree(Blast_ScoreFreq* sfp); */ NCBI_XBLAST_EXPORT Int2 -Blast_GetStdAlphabet(Uint1 alphabet_code, Uint1* residues, +Blast_GetStdAlphabet(Uint1 alphabet_code, Uint1* residues, Uint4 residue_size); /** Computes the parameters lambda, H K for use in calculating the @@ -846,16 +844,17 @@ Blast_FillResidueProbability(const Uint1* sequence, Int4 length, double * resPro NCBI_XBLAST_EXPORT Int2 BlastScoreBlkNuclMatrixCreate(BlastScoreBlk* sbp); -/** Returns a pointer to the static compiled in version of the +/** Returns a pointer to the static compiled in version of the * matrix. If name is NULL or the matrix is not compiled in * NULL is returned. * @param name matrix name [in] * @return pointer to matrix or NULL if not supported. */ -NCBI_XBLAST_EXPORT +NCBI_XBLAST_EXPORT SNCBIPackedScoreMatrix* BlastScoreBlkGetCompiledInMatrix(const char* name); #ifdef __cplusplus } #endif #endif /* !ALGO_BLAST_CORE__BLAST_STAT__H */ + diff --git a/src/lib/blast/blastn_score.cpp b/src/lib/blast/blastn_score.cpp new file mode 100644 index 000000000..42a941ec1 --- /dev/null +++ b/src/lib/blast/blastn_score.cpp @@ -0,0 +1,2465 @@ +#include +#include "blast_stat.h" +#include "blast_encoding.h" +#include "blast_def.h" +#include "blast_psi_priv.h" +#include "blast/ncbi_math.h" +#include "blast_setup.h" +#include "ncbi_std.h" +#include "blast_options.h" +#include "blast_encoding.h" + + + +#define BLAST_SCORE_RANGE_MAX (BLAST_SCORE_MAX - BLAST_SCORE_MIN) /**< maximum allowed range of BLAST scores. */ +#define BLAST_KARLIN_LAMBDA0_DEFAULT 0.5 /**< Initial guess for the value of Lambda in BlastKarlinLambdaNR */ +#define BLAST_KARLIN_K_SUMLIMIT_DEFAULT 0.0001 /**< K_SUMLIMIT_DEFAULT == sumlimit used in BlastKarlinLHtoK() */ + +#define BLAST_KARLIN_LAMBDA_ACCURACY_DEFAULT (1.e-5) /**< LAMBDA_ACCURACY_DEFAULT == accuracy to which Lambda should be calc'd */ + +#define BLAST_KARLIN_LAMBDA_ITER_DEFAULT 17 /**< LAMBDA_ITER_DEFAULT == no. of iterations in LambdaBis = ln(accuracy)/ln(2)*/ + +#define BLAST_KARLIN_LAMBDA0_DEFAULT 0.5 /**< Initial guess for the value of Lambda in BlastKarlinLambdaNR */ + +#define BLAST_KARLIN_K_ITER_MAX 100 /**< upper limit on iterations for BlastKarlinLHtoK */ + +/** Number of statistical parameters in each row of the precomputed tables. */ +#define BLAST_NUM_STAT_VALUES 11 /**< originally 8, now 11 to support Spouge's FSC. see notes below */ + + + +/** Holds values (gap-opening, extension, etc.) for a matrix. */ +typedef double array_of_8[BLAST_NUM_STAT_VALUES]; + +/** Used to temporarily store matrix values for retrieval. */ +typedef struct MatrixInfo { + char* name; /**< name of matrix (e.g., BLOSUM90). */ + array_of_8 *values; /**< The values (gap-opening, extension etc.). */ + Int4 *prefs; /**< Preferences for display. */ + Int4 max_number_values; /**< number of values (e.g., BLOSUM90_VALUES_MAX). */ +} MatrixInfo; + + +/** Karlin-Altschul parameter values for substitution scores 1 and -5. */ +static const array_of_8 blastn_values_1_5[] = { + { 0, 0, 1.39, 0.747, 1.38, 1.00, 0, 100 }, + { 3, 3, 1.39, 0.747, 1.38, 1.00, 0, 100 } +}; + +/** Karlin-Altschul parameter values for substitution scores 1 and -4. */ +static const array_of_8 blastn_values_1_4[] = { + { 0, 0, 1.383, 0.738, 1.36, 1.02, 0, 100 }, + { 1, 2, 1.36, 0.67, 1.2, 1.1, 0, 98 }, + { 0, 2, 1.26, 0.43, 0.90, 1.4, -1, 91 }, + { 2, 1, 1.35, 0.61, 1.1, 1.2, -1, 98 }, + { 1, 1, 1.22, 0.35, 0.72, 1.7, -3, 88 } +}; + +/** Karlin-Altschul parameter values for substitution scores 2 and -7. + * These parameters can only be applied to even scores. Any odd score must be + * rounded down to the nearest even number before calculating the e-value. + */ +static const array_of_8 blastn_values_2_7[] = { + { 0, 0, 0.69, 0.73, 1.34, 0.515, 0, 100 }, + { 2, 4, 0.68, 0.67, 1.2, 0.55, 0, 99 }, + { 0, 4, 0.63, 0.43, 0.90, 0.7, -1, 91 }, + { 4, 2, 0.675, 0.62, 1.1, 0.6, -1, 98 }, + { 2, 2, 0.61, 0.35, 0.72, 1.7, -3, 88 } +}; + +/** Karlin-Altschul parameter values for substitution scores 1 and -3. */ +static const array_of_8 blastn_values_1_3[] = { + { 0, 0, 1.374, 0.711, 1.31, 1.05, 0, 100 }, + { 2, 2, 1.37, 0.70, 1.2, 1.1, 0, 99 }, + { 1, 2, 1.35, 0.64, 1.1, 1.2, -1, 98 }, + { 0, 2, 1.25, 0.42, 0.83, 1.5, -2, 91 }, + { 2, 1, 1.34, 0.60, 1.1, 1.2, -1, 97 }, + { 1, 1, 1.21, 0.34, 0.71, 1.7, -2, 88 } +}; + +/** Karlin-Altschul parameter values for substitution scores 2 and -5. + * These parameters can only be applied to even scores. Any odd score must be + * rounded down to the nearest even number before calculating the e-value. + */ +static const array_of_8 blastn_values_2_5[] = { + { 0, 0, 0.675, 0.65, 1.1, 0.6, -1, 99 }, + { 2, 4, 0.67, 0.59, 1.1, 0.6, -1, 98 }, + { 0, 4, 0.62, 0.39, 0.78, 0.8, -2, 91 }, + { 4, 2, 0.67, 0.61, 1.0, 0.65, -2, 98 }, + { 2, 2, 0.56, 0.32, 0.59, 0.95, -4, 82 } +}; + +/** Karlin-Altschul parameter values for substitution scores 1 and -2. */ +static const array_of_8 blastn_values_1_2[] = { + { 0, 0, 1.28, 0.46, 0.85, 1.5, -2, 96 }, + { 2, 2, 1.33, 0.62, 1.1, 1.2, 0, 99 }, + { 1, 2, 1.30, 0.52, 0.93, 1.4, -2, 97 }, + { 0, 2, 1.19, 0.34, 0.66, 1.8, -3, 89 }, + { 3, 1, 1.32, 0.57, 1.0, 1.3, -1, 99 }, + { 2, 1, 1.29, 0.49, 0.92, 1.4, -1, 96 }, + { 1, 1, 1.14, 0.26, 0.52, 2.2, -5, 85 } +}; + +/** Karlin-Altschul parameter values for substitution scores 2 and -3. + * These parameters can only be applied to even scores. Any odd score must be + * rounded down to the nearest even number before calculating the e-value. + */ +static const array_of_8 blastn_values_2_3[] = { + { 0, 0, 0.55, 0.21, 0.46, 1.2, -5, 87 }, + { 4, 4, 0.63, 0.42, 0.84, 0.75, -2, 99 }, + { 2, 4, 0.615, 0.37, 0.72, 0.85, -3, 97 }, + { 0, 4, 0.55, 0.21, 0.46, 1.2, -5, 87 }, + { 3, 3, 0.615, 0.37, 0.68, 0.9, -3, 97 }, + { 6, 2, 0.63, 0.42, 0.84, 0.75, -2, 99 }, + { 5, 2, 0.625, 0.41, 0.78, 0.8, -2, 99 }, + { 4, 2, 0.61, 0.35, 0.68, 0.9, -3, 96 }, + { 2, 2, 0.515, 0.14, 0.33, 1.55, -9, 81 } +}; + +/** Karlin-Altschul parameter values for substitution scores 3 and -4. */ +static const array_of_8 blastn_values_3_4[] = { + { 6, 3, 0.389, 0.25, 0.56, 0.7, -5, 95}, + { 5, 3, 0.375, 0.21, 0.47, 0.8, -6, 92}, + { 4, 3, 0.351, 0.14, 0.35, 1.0, -9, 86}, + { 6, 2, 0.362, 0.16, 0.45, 0.8, -4, 88}, + { 5, 2, 0.330, 0.092, 0.28, 1.2, -13, 81}, + { 4, 2, 0.281, 0.046, 0.16, 1.8, -23, 69} +}; + +/** Karlin-Altschul parameter values for substitution scores 4 and -5. */ +static const array_of_8 blastn_values_4_5[] = { + { 0, 0, 0.22, 0.061, 0.22, 1.0, -15, 74 }, + { 6, 5, 0.28, 0.21, 0.47, 0.6 , -7, 93 }, + { 5, 5, 0.27, 0.17, 0.39, 0.7, -9, 90 }, + { 4, 5, 0.25, 0.10, 0.31, 0.8, -10, 83 }, + { 3, 5, 0.23, 0.065, 0.25, 0.9, -11, 76 } +}; + +/** Karlin-Altschul parameter values for substitution scores 1 and -1. */ +static const array_of_8 blastn_values_1_1[] = { + { 3, 2, 1.09, 0.31, 0.55, 2.0, -2, 99 }, + { 2, 2, 1.07, 0.27, 0.49, 2.2, -3, 97 }, + { 1, 2, 1.02, 0.21, 0.36, 2.8, -6, 92 }, + { 0, 2, 0.80, 0.064, 0.17, 4.8, -16, 72 }, + { 4, 1, 1.08, 0.28, 0.54, 2.0, -2, 98 }, + { 3, 1, 1.06, 0.25, 0.46, 2.3, -4, 96 }, + { 2, 1, 0.99, 0.17, 0.30, 3.3, -10, 90 } +}; + +/** Karlin-Altschul parameter values for substitution scores 3 and -2. */ +static const array_of_8 blastn_values_3_2[] = { + { 5, 5, 0.208, 0.030, 0.072, 2.9, -47, 77} +}; + +/** Karlin-Altschul parameter values for substitution scores 5 and -4. */ +static const array_of_8 blastn_values_5_4[] = { + { 10, 6, 0.163, 0.068, 0.16, 1.0, -19, 85 }, + { 8, 6, 0.146, 0.039, 0.11, 1.3, -29, 76 } +}; + + + +typedef struct BLAST_LetterProb { + char ch; /**< residue */ + double p; /**< probability of residue. */ +} BLAST_LetterProb; + +static BLAST_LetterProb nt_prob[] = { + { 'A', 25.00 }, + { 'C', 25.00 }, + { 'G', 25.00 }, + { 'T', 25.00 } +}; /**< nucleotide probabilities (25% each letter) */ + + +void** +_PSIDeallocateMatrix(void** matrix, unsigned int ncols) +{ + unsigned int i = 0; + + if (!matrix) + return NULL; + + for (i = 0; i < ncols; i++) { + sfree(matrix[i]); + } + sfree(matrix); + return NULL; +} +static Int4 +BlastKarlinEtoS_simple(double E, /* Expect value */ + const Blast_KarlinBlk* kbp, + Int8 searchsp) /* size of search space */ +{ + + double Lambda, K, H; /* parameters for Karlin statistics */ + Int4 S; +/* Smallest float that might not cause a floating point exception in + S = (Int4) (ceil( log((double)(K * searchsp / E)) / Lambda )); below. */ + const double kSmallFloat = 1.0e-297; + + Lambda = kbp->Lambda; + K = kbp->K; + H = kbp->H; + if (Lambda < 0. || K < 0. || H < 0.0) + { + return BLAST_SCORE_MIN; + } + + E = MAX(E, kSmallFloat); + + S = (Int4) (ceil( log((double)(K * searchsp / E)) / Lambda )); + return S; +} + +double +BLAST_KarlinStoE_simple(Int4 S, + Blast_KarlinBlk* kbp, + Int8 searchsp) /* size of search space. */ +{ + double Lambda, K, H; /* parameters for Karlin statistics */ + + Lambda = kbp->Lambda; + K = kbp->K; + H = kbp->H; + if (Lambda < 0. || K < 0. || H < 0.) { + return -1.; + } + + return (double) searchsp * exp((double)(-Lambda * S) + kbp->logK); + + + +} + + +static Blast_GumbelBlk* +s_BlastGumbelBlkNew() { + return (Blast_GumbelBlk*) calloc(1, sizeof(Blast_GumbelBlk)); +} +SBlastScoreMatrix* +SBlastScoreMatrixFree(SBlastScoreMatrix* matrix) +{ + if ( !matrix ) { + return NULL; + } + + if (matrix->data) { + matrix->data = (int**) _PSIDeallocateMatrix((void**) matrix->data, + (unsigned)matrix->ncols); + } + + /* Deallocate the matrix frequencies which is used by the + * nucleotide custom matrix reader. -RMH- + */ + if ( matrix->freqs ) + sfree(matrix->freqs); + + sfree(matrix); + return NULL; +} +BlastScoringOptions* +BlastScoringOptionsFree(BlastScoringOptions* options) + +{ + if (options == NULL) + return NULL; + + sfree(options->matrix); + sfree(options->matrix_path); + sfree(options); + + return NULL; +} + +Int4 BLAST_Gcd(Int4 a, Int4 b) +{ + Int4 c; + + b = ABS(b); + if (b > a) + c=a, a=b, b=c; + + while (b != 0) { + c = a%b; + a = b; + b = c; + } + return a; +} + +void** +_PSIAllocateMatrix(unsigned int ncols, unsigned int nrows, + unsigned int data_type_sz) +{ + void** retval = NULL; + unsigned int i = 0; + + retval = (void**) malloc(sizeof(void*) * ncols); + if ( !retval ) { + return NULL; + } + + for (i = 0; i < ncols; i++) { + retval[i] = (void*) calloc(nrows, data_type_sz); + if ( !retval[i] ) { + retval = _PSIDeallocateMatrix(retval, i); + break; + } + } + return retval; +} + + +static Int2 +BlastScoreBlkProteinMatrixRead(BlastScoreBlk* sbp, FILE *fp) +{ + char buf[512+3]; + char temp[512]; + char* cp,* lp; + char ch; + Int4 ** matrix; + Int4 * m; + Int4 score; + Uint4 a1cnt = 0, a2cnt = 0; + char a1chars[BLASTAA_SIZE], a2chars[BLASTAA_SIZE]; + long lineno = 0; + double xscore; + register int index1, index2; + int x_index, u_index, o_index, c_index; + const char kCommentChar = '#'; + const char* kTokenStr = " \t\n\r"; + + ASSERT(sbp->alphabet_size == BLASTAA_SIZE); + ASSERT(sbp->matrix); + ASSERT(sbp->matrix->ncols == BLASTAA_SIZE); + ASSERT(sbp->matrix->nrows == BLASTAA_SIZE); + + matrix = sbp->matrix->data; + + if (sbp->alphabet_code != BLASTNA_SEQ_CODE) { + for (index1 = 0; index1 < sbp->alphabet_size; index1++) + for (index2 = 0; index2 < sbp->alphabet_size; index2++) + matrix[index1][index2] = BLAST_SCORE_MIN; + } + + /* Read the residue names for the second alphabet */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + ++lineno; + if (strchr(buf, '\n') == NULL) { + return 2; + } + + if (buf[0] == kCommentChar) { + /* save the comment line in a linked list */ + *strchr(buf, '\n') = NULLB; + ListNodeCopyStr(&sbp->comments, 0, buf+1); + continue; + } + if ((cp = strchr(buf, kCommentChar)) != NULL) + *cp = NULLB; + lp = (char*)strtok(buf, kTokenStr); + if (lp == NULL) /* skip blank lines */ + continue; + while (lp != NULL) { + if (sbp->alphabet_code == BLASTAA_SEQ_CODE) + ch = AMINOACID_TO_NCBISTDAA[toupper((unsigned char)(*lp))]; + else if (sbp->alphabet_code == BLASTNA_SEQ_CODE) { + ch = IUPACNA_TO_BLASTNA[toupper((unsigned char)(*lp))]; + } else { + ch = *lp; + } + a2chars[a2cnt++] = ch; + lp = (char*)strtok(NULL, kTokenStr); + } + + break; /* Exit loop after reading one line. */ + } + + if (a2cnt <= 1) { + return 2; + } + + while (fgets(buf, sizeof(buf), fp) != NULL) { + ++lineno; + if ((cp = strchr(buf, '\n')) == NULL) { + return 2; + } + if ((cp = strchr(buf, kCommentChar)) != NULL) + *cp = NULLB; + if ((lp = (char*)strtok(buf, kTokenStr)) == NULL) + continue; + ch = *lp; + if ((cp = strtok(NULL, kTokenStr)) == NULL) { + return 2; + } + if (a1cnt >= DIM(a1chars)) { + return 2; + } + + if (sbp->alphabet_code == BLASTAA_SEQ_CODE) { + ch = AMINOACID_TO_NCBISTDAA[toupper((unsigned char) ch)]; + } else { + if (sbp->alphabet_code == BLASTNA_SEQ_CODE) { + ch = IUPACNA_TO_BLASTNA[toupper((unsigned char) ch)]; + } + } + a1chars[a1cnt++] = ch; + m = &matrix[(int)ch][0]; + index2 = 0; + while (cp != NULL) { + if (index2 >= (int) a2cnt) { + return 2; + } + strcpy(temp, cp); + + if (strcasecmp(temp, "na") == 0) { + score = BLAST_SCORE_MIN; + } else { + if (sscanf(temp, "%lg", &xscore) != 1) { + return 2; + } + /*xscore = MAX(xscore, BLAST_SCORE_1MIN);*/ + if (xscore > BLAST_SCORE_MAX || xscore < BLAST_SCORE_MIN) { + return 2; + } + xscore += (xscore >= 0. ? 0.5 : -0.5); + score = (Int4)xscore; + } + + m[(int)a2chars[index2++]] = score; + + cp = strtok(NULL, kTokenStr); + } + } + + if (a1cnt <= 1) { + return 2; + } + + /* Use the C scores for U and X scores for O characters; + if this is not done then they will never align to non-gap residues */ + x_index = AMINOACID_TO_NCBISTDAA['X']; + u_index = AMINOACID_TO_NCBISTDAA['U']; + o_index = AMINOACID_TO_NCBISTDAA['O']; + c_index = AMINOACID_TO_NCBISTDAA['C']; + for (index1 = 0; index1 < sbp->alphabet_size; index1++) { + matrix[u_index][index1] = matrix[c_index][index1]; + matrix[index1][u_index] = matrix[index1][c_index]; + matrix[o_index][index1] = matrix[x_index][index1]; + matrix[index1][o_index] = matrix[index1][x_index]; + } + + return 0; +} + + +static Int2 +BlastScoreBlkMaxScoreSet(BlastScoreBlk* sbp) +{ + Int4 score; + Int4 ** matrix; + Int2 index1, index2; + + sbp->loscore = BLAST_SCORE_MAX; + sbp->hiscore = BLAST_SCORE_MIN; + matrix = sbp->matrix->data; + for (index1=0; index1alphabet_size; index1++) + { + for (index2=0; index2alphabet_size; index2++) + { + score = matrix[index1][index2]; + if (score <= BLAST_SCORE_MIN || score >= BLAST_SCORE_MAX) + continue; + if (sbp->loscore > score) + sbp->loscore = score; + if (sbp->hiscore < score) + sbp->hiscore = score; + } + } + /* If the lo/hi-scores are BLAST_SCORE_MIN/BLAST_SCORE_MAX, (i.e., for + gaps), then use other scores. */ + + if (sbp->loscore < BLAST_SCORE_MIN) + sbp->loscore = BLAST_SCORE_MIN; + if (sbp->hiscore > BLAST_SCORE_MAX) + sbp->hiscore = BLAST_SCORE_MAX; + + return 0; +} + + +static Int2 +BlastScoreBlkNucleotideMatrixRead(BlastScoreBlk* sbp, FILE *fp) +{ + Int4 ** matrix; + double* freqs; + Int4 i = 0; + Int4 j = 0; + Int4 rowIdx,colIdx,val,base; + Int4 numFreqs = 0; + Int4 alphaSize = 0; + double fval; + register int index1, index2; + char fbuf[512+3]; + char alphabet[24]; + char *cp,*ncp,*lp; + double lambda_upper = 0; + double lambda_lower = 0; + double lambda = 0.5; + double sum; + double check; + const char kCommentChar = '#'; + const char* kTokenStr = " \t\n\r"; + + // Initialize matrix to default values + matrix = sbp->matrix->data; + for (index1 = 0; index1 < sbp->alphabet_size; index1++) + for (index2 = 0; index2 < sbp->alphabet_size; index2++) + matrix[index1][index2] = BLAST_SCORE_MIN; + + // Initialize matrix freqs to default values + freqs = sbp->matrix->freqs; + for (index1 = 0; index1 < sbp->alphabet_size; index1++) + freqs[index1] = 0; + + alphabet[0] = 0; + while ( fgets(fbuf,sizeof(fbuf),fp) ) { + if (strchr(fbuf, '\n') == NULL) { + return 2; + } + + /* initialize column pointer */ + cp = (char *)fbuf; + + /* eat whitespace */ + while( (*cp) && isspace(*cp) ) cp++; + + if (*cp == kCommentChar) { + /* special case FREQS line ( must exist ) */ + if ( (ncp = strstr( cp, (const char *)"FREQS" )) != NULL ) { + cp = ncp + 5; + /* eat whitespace */ + while( (*cp) && isspace(*cp) ) cp++; + + lp = (char*)strtok(cp, kTokenStr); + /* Missing values */ + if (lp == NULL) + return 2; + + numFreqs = 0; + while (lp != NULL) { + // Read Nucleotide + base = (int)IUPACNA_TO_BLASTNA[toupper((unsigned char)(*lp))]; + + lp = (char*)strtok(NULL, kTokenStr); + /* Expected a token pair */ + if ( lp == NULL ) + return 2; + + // Read Frequency + if ( sscanf(lp, "%lf", &fval ) != 1 ) + return( 2 ); + + // Store base/fval + freqs[base] = fval; + numFreqs++; + lp = (char*)strtok(NULL, kTokenStr); + } + }else { + /* save the comment line in a linked list */ + *strchr(cp, '\n') = NULLB; + ListNodeCopyStr(&sbp->comments, 0, cp); + } + continue; + } + + /* alphabet line */ + if ( isalpha(*cp) && !alphabet[0] ) { + j = 0; + lp = (char*)strtok(cp, kTokenStr); + while (lp != NULL) { + alphabet[j++] = toupper((unsigned char)(*lp)); + lp = (char*)strtok(NULL, kTokenStr); + } + alphabet[j] = 0; + alphaSize = j; + continue; + }else if ( isalpha(*cp) ) { + /* Chew off first alphabet character */ + cp++; + /* eat whitespace */ + while( (*cp) && isspace(*cp) ) cp++; + } + + /* Matrix data */ + if ( isdigit(*cp) || *cp == '-' ) { + j = 0; + lp = (char*)strtok(cp, kTokenStr); + rowIdx = (int)IUPACNA_TO_BLASTNA[toupper((unsigned char)alphabet[i])]; + while (lp != NULL) { + if ( sscanf(lp, "%d", &val ) != 1 ) + return( 2 ); + colIdx = (int)IUPACNA_TO_BLASTNA[toupper((unsigned char)alphabet[j++])]; + matrix[rowIdx][colIdx] = val; + lp = (char*)strtok(NULL, kTokenStr); + } + /* We should have as many values as we do characters in the + alphabet */ + if ( j != alphaSize ) + return( 2 ); + i++; + continue; + } + } + + /* Expected 4 base frequencies, and a square matrix */ + if ( numFreqs != 4 || i != alphaSize ) + return( 2 ); + + /* Calculate lambda for complexity adjusted scoring. This + scoring system was designed by Phil Green and used in the + cross_match package. It was also used in MaskerAid to make + wublast compatable with RepeatMasker. */ + do { + sum = 0; + check = 0; + for ( i = 0 ; i < sbp->alphabet_size ; i++ ) { + for ( j = 0 ; j < sbp->alphabet_size ; j++ ) { + if ( freqs[i] && freqs[j] ) + { + sum += freqs[i] * freqs[j] * + exp( lambda * matrix[i][j] ); + check += freqs[i] * freqs[j]; + } + } + } + ASSERT( ( check < (double)1.001 ) && ( check > (double)0.999 ) ); + if ( sum < 1.0 ) { + lambda_lower = lambda; + lambda *= 2.0; + } + } while ( sum < 1.0 ); + + lambda_upper = lambda; + + while ( lambda_upper - lambda_lower > (double).00001 ) { + lambda = ( lambda_lower + lambda_upper ) / 2.0; + sum = 0; + check = 0; + for ( i = 0 ; i < sbp->alphabet_size ; i++ ) + { + for ( j = 0 ; j < sbp->alphabet_size ; j++ ) + { + if ( freqs[i] && freqs[j] ) + { + sum += freqs[i] * freqs[j] * + exp( lambda * matrix[i][j] ); + check += freqs[i] * freqs[j]; + } + } + } + ASSERT( ( check < (double)1.001 ) && ( check > (double).999 ) ); + if ( sum >= 1.0 ) { + lambda_upper = lambda; + } + else { + lambda_lower = lambda; + } + } + sbp->matrix->lambda = lambda; + + /* The value of 15 is a gap, which is a sentinel between strands in + the ungapped extension algorithm. */ + for (index1=0; index1= 0 && aa < (int)sizeof(kNCBIstdaa)) { + aa = kNCBIstdaa[aa]; + } else if (islower((unsigned char) aa)) { + aa = toupper((unsigned char) aa); + } + + p = strchr(sm->symbols, aa); + return p ? (int)(p - sm->symbols) : -1; +} + +TNCBIScore NCBISM_GetScore(const SNCBIPackedScoreMatrix* sm, + int aa1, int aa2) +{ + int i1, i2; + i1 = NCBISM_GetIndex(sm, aa1); + i2 = NCBISM_GetIndex(sm, aa2); + if (i1 >=0 && i2 >= 0) { + return sm->scores[(size_t)i1 * strlen(sm->symbols) + (size_t)i2]; + } else { + return sm->defscore; + } +} + +long BLAST_Nint(double x) +{ + x += (x >= 0. ? 0.5 : -0.5); + return (long)x; +} +Int2 BlastScoreBlkNuclMatrixCreate(BlastScoreBlk* sbp) +{ + Int2 index1, index2, degen; + Int2 degeneracy[BLASTNA_SIZE+1]; + Int4 reward; /* reward for match of bases. */ + Int4 penalty; /* cost for mismatch of bases. */ + Int4** matrix; /* matrix to be populated. */ + /* How many of the first bases are ambiguous (four, of course). */ + const int k_number_non_ambig_bp = 4; + + ASSERT(sbp); + ASSERT(sbp->alphabet_size == BLASTNA_SIZE); + ASSERT(sbp->matrix); + ASSERT(sbp->matrix->ncols == BLASTNA_SIZE); + ASSERT(sbp->matrix->nrows == BLASTNA_SIZE); + + reward = sbp->reward; + penalty = sbp->penalty; + matrix = sbp->matrix->data; + + for (index1 = 0; index1Lambda = kbp_from->Lambda; + kbp_to->K = kbp_from->K; + kbp_to->logK = kbp_from->logK; + kbp_to->H = kbp_from->H; + kbp_to->paramC = kbp_from->paramC; + return 0; +} +static +int /* bool */ s_NCBISM_StartsWith(const char* str, const char* pfx) +{ + for ( ; *pfx; ++str, ++pfx) { + if (tolower((unsigned char)*str) != *pfx) { + return 0; + } + } + return 1; +} +const SNCBIPackedScoreMatrix* NCBISM_GetStandardMatrix(const char* name) { + switch (name[0]) { + case 'B': + case 'b': + if (!s_NCBISM_StartsWith(name, "blosum")) { + return NULL; + } + + + case 'P': + case 'p': + if (!s_NCBISM_StartsWith(name, "pam")) { + return NULL; + } + + case 'I': + case 'i': + if (!s_NCBISM_StartsWith(name, "identity")) { + return NULL; + } + return &NCBISM_Identity; + + default: + return NULL; + } +} + + static Int2 + BlastScoreBlkProteinMatrixLoad(BlastScoreBlk *sbp) { + Int2 status = 0; + Int4 **matrix = NULL; + int i, j; /* loop indices */ + int x_index, u_index, o_index, c_index; + const SNCBIPackedScoreMatrix *psm; + + ASSERT(sbp); + psm = NCBISM_GetStandardMatrix(sbp->name); + if (psm == NULL) + return 1; + + ASSERT(sbp->alphabet_size == BLASTAA_SIZE); + ASSERT(sbp->matrix); + ASSERT(sbp->matrix->ncols == BLASTAA_SIZE); + ASSERT(sbp->matrix->nrows == BLASTAA_SIZE); + + matrix = sbp->matrix->data; + + /* Initialize with BLAST_SCORE_MIN */ + for (i = 0; i < sbp->alphabet_size; i++) { + for (j = 0; j < sbp->alphabet_size; j++) { + matrix[i][j] = BLAST_SCORE_MIN; + } + } + + for (i = 0; i < sbp->alphabet_size; i++) { + for (j = 0; j < sbp->alphabet_size; j++) { + /* skip special characters */ + if (i == AMINOACID_TO_NCBISTDAA['U'] || + i == AMINOACID_TO_NCBISTDAA['O'] || + i == AMINOACID_TO_NCBISTDAA['-'] || + j == AMINOACID_TO_NCBISTDAA['U'] || + j == AMINOACID_TO_NCBISTDAA['O'] || + j == AMINOACID_TO_NCBISTDAA['-']) { + continue; + } + matrix[i][j] = NCBISM_GetScore((const SNCBIPackedScoreMatrix *) psm, + i, j); + } + } + + /* Use the C scores for U and X scores for the O characters; + if this is not done then they will never align to non-gap residues */ + x_index = AMINOACID_TO_NCBISTDAA['X']; + u_index = AMINOACID_TO_NCBISTDAA['U']; + o_index = AMINOACID_TO_NCBISTDAA['O']; + c_index = AMINOACID_TO_NCBISTDAA['C']; + for (i = 0; i < sbp->alphabet_size; i++) { + matrix[u_index][i] = matrix[c_index][i]; + matrix[i][u_index] = matrix[i][c_index]; + matrix[o_index][i] = matrix[x_index][i]; + matrix[i][o_index] = matrix[i][x_index]; + } + + return status; + } + + + +Int2 +Blast_ScoreBlkMatrixFill(BlastScoreBlk* sbp, GET_MATRIX_PATH get_path) +{ + bool matrix_found = FALSE; + Int2 status = 0; + + /* For nucleotide case we first create a default matrix, based on the + match and mismatch scores. */ + if (sbp->alphabet_code == BLASTNA_SEQ_CODE) { + /* RMBLASTN supports reading a custom matrix. Currently + * I am using the sbp->read_in_matrix parameter to tell if + * we should invoke the matrix reader. + * -RMH- + */ + if ( sbp->read_in_matrix && get_path ) + { + // Read in custom rmblastn matrix + matrix_found = FALSE; + } + else + { + if ( (status=BlastScoreBlkNuclMatrixCreate(sbp)) != 0) + return status; + matrix_found = TRUE; + } + } + else + { /* Try to get compiled in matrix for proteins. */ + status = BlastScoreBlkProteinMatrixLoad(sbp); + if (status == 0) + matrix_found = TRUE; + } + + /*if (matrix_found == FALSE && sbp->read_in_matrix && get_path) { + // -RMH- Changed to FALSE + char* matrix_path = get_path(sbp->name, FALSE); + if (matrix_path) { + + FILE *fp = NULL; + char* full_matrix_path = NULL; + int path_len = strlen(matrix_path); + int buflen = path_len + strlen(sbp->name); + + full_matrix_path = (char*) malloc((buflen + 1) * sizeof(char)); + if (!full_matrix_path) { + return -1; + } + strncpy(full_matrix_path, matrix_path, buflen); + strncat(full_matrix_path, sbp->name, buflen - path_len); + + sfree(matrix_path); + + if ( (fp=fopen(full_matrix_path, "r")) == NULL) { + return -1; + } + sfree(full_matrix_path); + + if (sbp->alphabet_code == BLASTNA_SEQ_CODE) { + // nucleotide blast ( rmblastn ) which can utilize a + // a custom matrix -RMH- + if ((status=BlastScoreBlkNucleotideMatrixRead(sbp, fp)) != 0) + { + fclose(fp); + return status; + } + }else { + // protein blast + if ( (status=BlastScoreBlkProteinMatrixRead(sbp, fp)) != 0) + { + fclose(fp); + return status; + } + } + + fclose(fp); + matrix_found = TRUE; + } + }*/ + + if (matrix_found == FALSE) + return -1; + + if ( (status=BlastScoreBlkMaxScoreSet(sbp)) != 0) + return status; + + return status; +} + + +SBlastScoreMatrix* +SBlastScoreMatrixNew(size_t ncols, size_t nrows) +{ + SBlastScoreMatrix* retval = NULL; + + retval = (SBlastScoreMatrix*) calloc(1, sizeof(SBlastScoreMatrix)); + if ( !retval ) { + return SBlastScoreMatrixFree(retval); + } + + retval->data = (int**) _PSIAllocateMatrix((unsigned)ncols, (unsigned)nrows, sizeof(int)); + if ( !retval->data ) { + return SBlastScoreMatrixFree(retval); + } + + /* Allocate additional attributes for use with custom + * nucleotide matrices. -RMH- + */ + retval->freqs = (double *) calloc(ncols, sizeof(double)); + retval->lambda = 0; + + retval->ncols = ncols; + retval->nrows = nrows; + return retval; +} + +BlastScoreBlk* +BlastScoreBlkNew(Uint1 alphabet, Int4 number_of_contexts) + +{ +BlastScoreBlk* sbp; +char* use_old_fsc; + +sbp = (BlastScoreBlk*) calloc(1, sizeof(BlastScoreBlk)); + +if ( !sbp ) { +return NULL; +} + +sbp->alphabet_code = alphabet; +if (alphabet != BLASTNA_SEQ_CODE) { +sbp->alphabet_size = BLASTAA_SIZE; +} else { +sbp->alphabet_size = BLASTNA_SIZE; +} + +/* set the alphabet type (protein or not). */ +switch (alphabet) { +case BLASTAA_SEQ_CODE: +sbp->protein_alphabet = TRUE; +break; +case BLASTNA_SEQ_CODE: +sbp->protein_alphabet = FALSE; +break; +default: +break; +} + +sbp->matrix = SBlastScoreMatrixNew(sbp->alphabet_size, sbp->alphabet_size); +if (sbp->matrix == NULL) { +return BlastScoreBlkFree(sbp); +} +sbp->scale_factor = 1.0; + +/* FSCOLD: to switch back to original FSC, comment out the following line */ +use_old_fsc = getenv("OLD_FSC"); +if (!use_old_fsc) sbp->gbp = s_BlastGumbelBlkNew(); + +sbp->number_of_contexts = number_of_contexts; +sbp->sfp = (Blast_ScoreFreq**) + calloc(sbp->number_of_contexts, sizeof(Blast_ScoreFreq*)); +sbp->kbp_std = (Blast_KarlinBlk**) + calloc(sbp->number_of_contexts, sizeof(Blast_KarlinBlk*)); +sbp->kbp_gap_std = (Blast_KarlinBlk**) + calloc(sbp->number_of_contexts, sizeof(Blast_KarlinBlk*)); +sbp->kbp_psi = (Blast_KarlinBlk**) + calloc(sbp->number_of_contexts, sizeof(Blast_KarlinBlk*)); +sbp->kbp_gap_psi = (Blast_KarlinBlk**) + calloc(sbp->number_of_contexts, sizeof(Blast_KarlinBlk*)); + +return sbp; +} +static Blast_GumbelBlk* +s_BlastGumbelBlkFree(Blast_GumbelBlk* gbp) { + if ( !gbp) return NULL; + sfree(gbp); + return NULL; +} + +Blast_KarlinBlk* +Blast_KarlinBlkFree(Blast_KarlinBlk* kbp) + +{ + sfree(kbp); + + return kbp; +} + +SPsiBlastScoreMatrix* +SPsiBlastScoreMatrixFree(SPsiBlastScoreMatrix* matrix) +{ + if ( !matrix ) { + return NULL; + } + + if (matrix->freq_ratios) { + matrix->freq_ratios = (double**) _PSIDeallocateMatrix((void**) + matrix->freq_ratios, + (unsigned)matrix->pssm->ncols); + } + + matrix->pssm = SBlastScoreMatrixFree(matrix->pssm); + matrix->kbp = Blast_KarlinBlkFree(matrix->kbp); + sfree(matrix); + return NULL; +} + + +Blast_ScoreFreq* +Blast_ScoreFreqFree(Blast_ScoreFreq* sfp) +{ + if (sfp == NULL) + return NULL; + + if (sfp->sprob0 != NULL) + sfree(sfp->sprob0); + sfree(sfp); + return sfp; +} +BlastScoreBlk* +BlastScoreBlkFree(BlastScoreBlk* sbp) + +{ + Int4 index; + if (sbp == NULL) + return NULL; + + for (index=0; indexnumber_of_contexts; index++) { + if (sbp->sfp) + sbp->sfp[index] = Blast_ScoreFreqFree(sbp->sfp[index]); + if (sbp->kbp_std) + sbp->kbp_std[index] = Blast_KarlinBlkFree(sbp->kbp_std[index]); + if (sbp->kbp_gap_std) + sbp->kbp_gap_std[index] = Blast_KarlinBlkFree(sbp->kbp_gap_std[index]); + if (sbp->kbp_psi) + sbp->kbp_psi[index] = Blast_KarlinBlkFree(sbp->kbp_psi[index]); + if (sbp->kbp_gap_psi) + sbp->kbp_gap_psi[index] = Blast_KarlinBlkFree(sbp->kbp_gap_psi[index]); + } + if (sbp->kbp_ideal) + sbp->kbp_ideal = Blast_KarlinBlkFree(sbp->kbp_ideal); + if (sbp->gbp) + sbp->gbp = s_BlastGumbelBlkFree(sbp->gbp); + sfree(sbp->sfp); + sbp->kbp = NULL; + sbp->kbp_gap = NULL; + sfree(sbp->kbp_std); + sfree(sbp->kbp_psi); + sfree(sbp->kbp_gap_std); + sfree(sbp->kbp_gap_psi); + sbp->matrix = SBlastScoreMatrixFree(sbp->matrix); + sbp->comments = ListNodeFreeData(sbp->comments); + if (sbp->name) { + sfree(sbp->name); + } + sbp->psi_matrix = SPsiBlastScoreMatrixFree(sbp->psi_matrix); + sfree(sbp->ambiguous_res); + sfree(sbp); + + return NULL; +} +Blast_KarlinBlk* +Blast_KarlinBlkNew(void) + +{ + Blast_KarlinBlk* kbp; + + kbp = (Blast_KarlinBlk*) calloc(1, sizeof(Blast_KarlinBlk)); + + return kbp; +} +Blast_ResFreq* +Blast_ResFreqNew(const BlastScoreBlk* sbp) +{ + Blast_ResFreq* rfp; + + if (sbp == NULL) + { + return NULL; + } + + rfp = (Blast_ResFreq*) calloc(1, sizeof(Blast_ResFreq)); + if (rfp == NULL) + return NULL; + + rfp->alphabet_code = sbp->alphabet_code; + + rfp->prob0 = (double*) calloc(sbp->alphabet_size, sizeof(double)); + if (rfp->prob0 == NULL) + { + rfp = Blast_ResFreqFree(rfp); + return rfp; + } + rfp->prob = rfp->prob0 - sbp->alphabet_start; + + return rfp; +} +static Int2 +BlastScoreFreqCalc(const BlastScoreBlk* sbp, Blast_ScoreFreq* sfp, Blast_ResFreq* rfp1, Blast_ResFreq* rfp2) +{ + Int4 ** matrix; + Int4 score, obs_min, obs_max; + double score_sum, score_avg; + Int2 alphabet_start, alphabet_end, index1, index2; + + if (sbp == NULL || sfp == NULL) + return 1; + + if (sbp->loscore < sfp->score_min || sbp->hiscore > sfp->score_max) + return 1; + + for (score = sfp->score_min; score <= sfp->score_max; score++) + sfp->sprob[score] = 0.0; + + matrix = sbp->matrix->data; + + alphabet_start = sbp->alphabet_start; + alphabet_end = alphabet_start + sbp->alphabet_size; + for (index1=alphabet_start; index1= sbp->loscore) + { + sfp->sprob[score] += rfp1->prob[index1] * rfp2->prob[index2]; + } + } + } + + score_sum = 0.; + obs_min = obs_max = BLAST_SCORE_MIN; + for (score = sfp->score_min; score <= sfp->score_max; score++) + { + if (sfp->sprob[score] > 0.) + { + score_sum += sfp->sprob[score]; + obs_max = score; + if (obs_min == BLAST_SCORE_MIN) + obs_min = score; + } + } + sfp->obs_min = obs_min; + sfp->obs_max = obs_max; + + score_avg = 0.0; + if (score_sum > 0.0001 || score_sum < -0.0001) + { + for (score = obs_min; score <= obs_max; score++) + { + sfp->sprob[score] /= score_sum; + score_avg += score * sfp->sprob[score]; + } + } + sfp->score_avg = score_avg; + + return 0; +} + + + +Int2 +Blast_ScoreBlkKbpIdealCalc(BlastScoreBlk* sbp) + +{ + Blast_ResFreq* stdrfp = NULL; + Blast_ScoreFreq* sfp = NULL; + Int2 status = 0; + + if ( !sbp ) { + return (status = 1); + } + + stdrfp = Blast_ResFreqNew(sbp); + Blast_ResFreqStdComp(sbp, stdrfp); + sfp = Blast_ScoreFreqNew(sbp->loscore, sbp->hiscore); + BlastScoreFreqCalc(sbp, sfp, stdrfp, stdrfp); + sbp->kbp_ideal = Blast_KarlinBlkNew(); + Blast_KarlinBlkUngappedCalc(sbp->kbp_ideal, sfp); + + stdrfp = Blast_ResFreqFree(stdrfp); + sfp = Blast_ScoreFreqFree(sfp); + + return status; +} +Blast_ResFreq* +Blast_ResFreqFree(Blast_ResFreq* rfp) +{ + if (rfp == NULL) + return NULL; + + if (rfp->prob0 != NULL) + sfree(rfp->prob0); + + sfree(rfp); + + return rfp; +} + +static double +BlastKarlinLHtoK(Blast_ScoreFreq* sfp, double lambda, double H) +{ + /*The next array stores the probabilities of getting each possible + score in an alignment of fixed length; the array is shifted + during part of the computation, so that + entry 0 is for score 0. */ + double *alignmentScoreProbabilities = NULL; + Int4 low; /* Lowest score (must be negative) */ + Int4 high; /* Highest score (must be positive) */ + Int4 range; /* range of scores, computed as high - low*/ + double K; /* local copy of K to return*/ + int i; /*loop index*/ + int iterCounter; /*counter on iterations*/ + Int4 divisor; /*candidate divisor of all scores with + non-zero probabilities*/ + /*highest and lowest possible alignment scores for current length*/ + Int4 lowAlignmentScore, highAlignmentScore; + Int4 first, last; /*loop indices for dynamic program*/ + register double innerSum; + double oldsum, oldsum2; /* values of innerSum on previous + iterations*/ + double outerSum; /* holds sum over j of (innerSum + for iteration j/j)*/ + + double score_avg; /*average score*/ + /*first term to use in the closed form for the case where + high == 1 or low == -1, but not both*/ + double firstTermClosedForm; /*usually store H/lambda*/ + int iterlimit; /*upper limit on iterations*/ + double sumlimit; /*lower limit on contributions + to sum over scores*/ + + /*array of score probabilities reindexed so that low is at index 0*/ + double *probArrayStartLow; + + /*pointers used in dynamic program*/ + double *ptrP, *ptr1, *ptr2, *ptr1e; + double expMinusLambda; /*e^^(-Lambda) */ + + if (lambda <= 0. || H <= 0.) { + /* Theory dictates that H and lambda must be positive, so + * return -1 to indicate an error */ + return -1.; + } + + /*Karlin-Altschul theory works only if the expected score + is negative*/ + if (sfp->score_avg >= 0.0) { + return -1.; + } + + low = sfp->obs_min; + high = sfp->obs_max; + range = high - low; + + probArrayStartLow = &sfp->sprob[low]; + /* Look for the greatest common divisor ("delta" in Appendix of PNAS 87 of + Karlin&Altschul (1990) */ + for (i = 1, divisor = -low; i <= range && divisor > 1; ++i) { + if (probArrayStartLow[i] != 0.0) + divisor = BLAST_Gcd(divisor, i); + } + + high /= divisor; + low /= divisor; + lambda *= divisor; + + range = high - low; + + firstTermClosedForm = H/lambda; + expMinusLambda = exp((double) -lambda); + + if (low == -1 && high == 1) { + K = (sfp->sprob[low*divisor] - sfp->sprob[high*divisor]) * + (sfp->sprob[low*divisor] - sfp->sprob[high*divisor]) / sfp->sprob[low*divisor]; + return(K); + } + + if (low == -1 || high == 1) { + if (high != 1) { + score_avg = sfp->score_avg / divisor; + firstTermClosedForm + = (score_avg * score_avg) / firstTermClosedForm; + } + return firstTermClosedForm * (1.0 - expMinusLambda); + } + + sumlimit = BLAST_KARLIN_K_SUMLIMIT_DEFAULT; + iterlimit = BLAST_KARLIN_K_ITER_MAX; + + alignmentScoreProbabilities = + (double *)calloc((iterlimit*range + 1), sizeof(*alignmentScoreProbabilities)); + if (alignmentScoreProbabilities == NULL) + return -1.; + + outerSum = 0.; + lowAlignmentScore = highAlignmentScore = 0; + alignmentScoreProbabilities[0] = innerSum = oldsum = oldsum2 = 1.; + + for (iterCounter = 0; + ((iterCounter < iterlimit) && (innerSum > sumlimit)); + outerSum += innerSum /= ++iterCounter) { + first = last = range; + lowAlignmentScore += low; + highAlignmentScore += high; + /*dynamic program to compute P(i,j)*/ + for (ptrP = alignmentScoreProbabilities + + (highAlignmentScore-lowAlignmentScore); + ptrP >= alignmentScoreProbabilities; + *ptrP-- =innerSum) { + ptr1 = ptrP - first; + ptr1e = ptrP - last; + ptr2 = probArrayStartLow + first; + for (innerSum = 0.; ptr1 >= ptr1e; ) { + innerSum += *ptr1 * *ptr2; + ptr1--; + ptr2++; + } + if (first) + --first; + if (ptrP - alignmentScoreProbabilities <= range) + --last; + } + /* Horner's rule */ + innerSum = *++ptrP; + for( i = lowAlignmentScore + 1; i < 0; i++ ) { + innerSum = *++ptrP + innerSum * expMinusLambda; + } + innerSum *= expMinusLambda; + + for (; i <= highAlignmentScore; ++i) + innerSum += *++ptrP; + oldsum2 = oldsum; + oldsum = innerSum; + } + + + + +#ifdef ADD_GEOMETRIC_TERMS_TO_K + /*old code assumed that the later terms in sum were + asymptotically comparable to those of a geometric + progression, and tried to speed up convergence by + guessing the estimated ratio between sucessive terms + and using the explicit terms of a geometric progression + to speed up convergence. However, the assumption does not + always hold, and convergenece of the above code is fast + enough in practice*/ + /* Terms of geometric progression added for correction */ + { + double ratio; /* fraction used to generate the + geometric progression */ + + ratio = oldsum / oldsum2; + if (ratio >= (1.0 - sumlimit*0.001)) { + K = -1.; + if (alignmentScoreProbabilities != NULL) + sfree(alignmentScoreProbabilities); + return K; + } + sumlimit *= 0.01; + while (innerSum > sumlimit) { + oldsum *= ratio; + outerSum += innerSum = oldsum / ++iterCounter; + } + } +#endif + + K = -exp((double)-2.0*outerSum) / + (firstTermClosedForm*BLAST_Expm1(-(double)lambda)); + + if (alignmentScoreProbabilities != NULL) + sfree(alignmentScoreProbabilities); + + return K; +} + + +static Int2 +BlastScoreChk(Int4 lo, Int4 hi) +{ + if (lo >= 0 || hi <= 0 || + lo < BLAST_SCORE_MIN || hi > BLAST_SCORE_MAX) + return 1; + + if (hi - lo > BLAST_SCORE_RANGE_MAX) + return 1; + + return 0; +} + +double BLAST_Expm1(double x) +{ + double absx = ABS(x); + + if (absx > .33) + return exp(x) - 1.; + + if (absx < 1.e-16) + return x; + + return x * (1. + x * + (1./2. + x * + (1./6. + x * + (1./24. + x * + (1./120. + x * + (1./720. + x * + (1./5040. + x * + (1./40320. + x * + (1./362880. + x * + (1./3628800. + x * + (1./39916800. + x * + (1./479001600. + + x/6227020800.)))))))))))); +} + +double BLAST_Powi(double x, Int4 n) +{ + double y; + + if (n == 0) + return 1.; + + if (x == 0.) { + if (n < 0) { + return HUGE_VAL; + } + return 0.; + } + + if (n < 0) { + x = 1./x; + n = -n; + } + + y = 1.; + while (n > 0) { + if (n & 1) + y *= x; + n /= 2; + x *= x; + } + return y; +} + +static Int2 +s_AdjustGapParametersByGcd(array_of_8* normal, array_of_8* linear, int size, Int4* gap_existence_max, Int4* gap_extend_max, int divisor) +{ + if (divisor == 1) + return 0; + + if (size <=0) + return 1; + + (*gap_existence_max) *= divisor; + (*gap_extend_max) *= divisor; + + if (normal) + { + int i; + + for (i=0; i 0) + *normal = static_cast(BlastMemDup(kValues, (*array_size) * sizeof(array_of_8))); + if (kValues_non_affine) + *non_affine = static_cast(BlastMemDup(kValues_non_affine, sizeof(array_of_8))); + + status = s_AdjustGapParametersByGcd(*normal, *non_affine, *array_size, gap_open_max, gap_extend_max, divisor); + } + + return status; +} + +static double +BlastKarlinLtoH(Blast_ScoreFreq* sfp, double lambda) +{ + Int4 score; + double H, etonlam, sum, scale; + + double *probs = sfp->sprob; + Int4 low = sfp->obs_min, high = sfp->obs_max; + + if (lambda < 0.) { + return -1.; + } + if (BlastScoreChk(low, high) != 0) return -1.; + + etonlam = exp( - lambda ); + sum = low * probs[low]; + for( score = low + 1; score <= high; score++ ) { + sum = score * probs[score] + etonlam * sum; + } + + scale = BLAST_Powi( etonlam, high ); + if( scale > 0.0 ) { + H = lambda * sum/scale; + } else { /* Underflow of exp( -lambda * high ) */ + H = lambda * exp( lambda * high + log(sum) ); + } + return H; +} + +Int2 +Blast_KarlinBlkNuclGappedCalc(Blast_KarlinBlk* kbp, Int4 gap_open, + Int4 gap_extend, Int4 reward, Int4 penalty, + Blast_KarlinBlk* kbp_ungap, + bool* round_down, + Blast_Message** error_return) +{ + const int kGapOpenIndex = 0; + const int kGapExtIndex = 1; + const int kLambdaIndex = 2; + const int kKIndex = 3; + const int kHIndex = 4; + int num_combinations = 0; + int gap_open_max, gap_extend_max; + array_of_8* normal=NULL; + array_of_8* linear=NULL; + Int2 status = s_GetNuclValuesArray(reward, + penalty, + &num_combinations, + &normal, + &linear, + &gap_open_max, + &gap_extend_max, + round_down, + error_return); + + if (status) + { + sfree(normal); + sfree(linear); + return status; + } + + ASSERT(kbp && kbp_ungap); + + + /* Try to find the table entry corresponding to input gap costs. */ + if (gap_open == 0 && gap_extend == 0 && linear) + { + kbp->Lambda = linear[0][kLambdaIndex]; + kbp->K = linear[0][kKIndex]; + kbp->logK = log(kbp->K); + kbp->H = linear[0][kHIndex]; + } + else + { + int index=0; + for (index = 0; index < num_combinations; ++index) { + if (normal[index][kGapOpenIndex] == gap_open && + normal[index][kGapExtIndex] == gap_extend) { + kbp->Lambda = normal[index][kLambdaIndex]; + kbp->K = normal[index][kKIndex]; + kbp->logK = log(kbp->K); + kbp->H = normal[index][kHIndex]; + break; + } + } + + /* If gap costs are not found in the table, check if they belong to the + infinite domain, where ungapped values of the parameters can be used. */ + if (index == num_combinations) { + /* If gap costs are larger than maximal provided in tables, copy + the values from the ungapped Karlin block. */ + if (gap_open >= gap_open_max && gap_extend >= gap_extend_max) { + Blast_KarlinBlkCopy(kbp, kbp_ungap); + } else if (error_return) { + char buffer[8192]; + int i=0; + int len=0; + /* Unsupported gap costs combination. */ + sprintf(buffer, "Gap existence and extension values %ld and %ld " + "are not supported for substitution scores %ld and %ld\n", + (long) gap_open, (long) gap_extend, (long) reward, (long) penalty); + for (i = 0; i < num_combinations; ++i) + { + len = (int)strlen(buffer); + sprintf(buffer+len, "%ld and %ld are supported existence and extension values\n", + (long) normal[i][kGapOpenIndex], (long) normal[i][kGapExtIndex]); + } + len = (int)strlen(buffer); + sprintf(buffer+len, "%ld and %ld are supported existence and extension values\n", + (long) gap_open_max, (long) gap_extend_max); + len = (int)strlen(buffer); + sprintf(buffer+len, "Any values more stringent than %ld and %ld are supported\n", + (long) gap_open_max, (long) gap_extend_max); + Blast_MessageWrite(error_return, eBlastSevError, kBlastMessageNoContext, buffer); + sfree(normal); + sfree(linear); + return 1; + } + } + } + + sfree(normal); + sfree(linear); + return 0; +} +char* BLAST_StrToUpper(const char* string) +{ + char* retval = NULL; /* the return value */ + char* p = NULL; /* auxiliary pointer */ + + if ( ! string ) { + return NULL; + } + + retval = strdup(string); + if ( !retval ) { + return NULL; + } + + for (p = retval; *p != NULLB; p++) { + *p = toupper((unsigned char)(*p)); + } + return retval; +} +#define SAFE_CAST_INT_TO_BOOLEAN(p) (((p) != 0) ? TRUE : FALSE) +bool Blast_SubjectIsNucleotide(EBlastProgramType p) +{ return SAFE_CAST_INT_TO_BOOLEAN(p & NUCLEOTIDE_SUBJECT_MASK); } +bool Blast_QueryIsNucleotide(EBlastProgramType p) +{ return SAFE_CAST_INT_TO_BOOLEAN(p & NUCLEOTIDE_QUERY_MASK); } + +bool Blast_ProgramIsNucleotide(EBlastProgramType p) +{ return Blast_QueryIsNucleotide(p) && Blast_SubjectIsNucleotide(p) ;} + + +Int2 +Blast_ScoreBlkMatrixInit(EBlastProgramType program_number, + const BlastScoringOptions* scoring_options, + BlastScoreBlk* sbp, + GET_MATRIX_PATH get_path) +{ + Int2 status = 0; + + if ( !sbp || !scoring_options ) { + + return 1; + } + + /* Matrix only scoring is used to disable the greedy extension + optimisations which avoid use of a full-matrix. This is + currently only turned on in RMBlastN -RMH- */ + sbp->matrix_only_scoring = FALSE; + + if (program_number == eBlastTypeBlastn) { + + BLAST_ScoreSetAmbigRes(sbp, 'N'); + BLAST_ScoreSetAmbigRes(sbp, '-'); + + /* If reward/penalty are both zero the calling program is + * indicating that a matrix must be used to score both the + * ungapped and gapped alignments. Set the new + * matrix_only_scoring. For now reset reward/penalty to + * allowed blastn values so that extraneous KA stats can be + * performed without error. -RMH- + */ + if ( scoring_options->penalty == 0 && scoring_options->reward == 0 ) + { + sbp->matrix_only_scoring = TRUE; + sbp->penalty = BLAST_PENALTY; + sbp->reward = BLAST_REWARD; + }else { + sbp->penalty = scoring_options->penalty; + sbp->reward = scoring_options->reward; + } + + if (scoring_options->matrix && *scoring_options->matrix != NULLB) { + + sbp->read_in_matrix = TRUE; + sbp->name = strdup(scoring_options->matrix); + + } else { + char buffer[50]; + sbp->read_in_matrix = FALSE; + sprintf(buffer, "blastn matrix:%ld %ld", + (long) sbp->reward, (long) sbp->penalty); + sbp->name = strdup(buffer); + } + + } else { + sbp->read_in_matrix = TRUE; + BLAST_ScoreSetAmbigRes(sbp, 'X'); + sbp->name = BLAST_StrToUpper(scoring_options->matrix); + } + status = Blast_ScoreBlkMatrixFill(sbp, get_path); + if (status) { + return status; + } + + return status; +} +Int2 BlastScoringOptionsSetMatrix(BlastScoringOptions* opts, + const char* matrix_name) +{ + Uint4 i; + + if (matrix_name) { + sfree(opts->matrix); + opts->matrix = strdup(matrix_name); + /* Make it all upper case */ + for (i=0; imatrix); ++i) + opts->matrix[i] = toupper((unsigned char) opts->matrix[i]); + } + return 0; +} + +Int2 +BLAST_FillScoringOptions(BlastScoringOptions* options, + EBlastProgramType program_number, bool greedy_extension, Int4 penalty, Int4 reward, + const char *matrix, Int4 gap_open, Int4 gap_extend) +{ + if (!options) + return BLASTERR_INVALIDPARAM; + + if (/*program_number != eBlastTypeBlastn && + program_number != eBlastTypePhiBlastn*/ + !Blast_ProgramIsNucleotide(program_number)) {/* protein-protein options. */ + /* If matrix name is not provided, keep the default "BLOSUM62" value filled in + BlastScoringOptionsNew, otherwise reset it. */ + if (matrix) + BlastScoringOptionsSetMatrix(options, matrix); + } else { /* nucleotide-nucleotide options. */ + if (penalty) + options->penalty = penalty; + if (reward) + options->reward = reward; + + if (greedy_extension) { + options->gap_open = BLAST_GAP_OPEN_MEGABLAST; + options->gap_extend = BLAST_GAP_EXTN_MEGABLAST; + } else { + options->gap_open = BLAST_GAP_OPEN_NUCL; + options->gap_extend = BLAST_GAP_EXTN_NUCL; + } + } + if (gap_open >= 0) + options->gap_open = gap_open; + if (gap_extend >= 0) + options->gap_extend = gap_extend; + + options->program_number = program_number; + + return 0; +} +Int2 +BLAST_ScoreSetAmbigRes(BlastScoreBlk* sbp, char ambiguous_res) + +{ + Int2 index; + Uint1* ambig_buffer; + + if (sbp == NULL) + return 1; + + if (sbp->ambig_occupy >= sbp->ambig_size) + { + sbp->ambig_size += 5; + ambig_buffer = (Uint1 *) calloc(sbp->ambig_size, sizeof(Uint1)); + for (index=0; indexambig_occupy; index++) + { + ambig_buffer[index] = sbp->ambiguous_res[index]; + } + sfree(sbp->ambiguous_res); + sbp->ambiguous_res = ambig_buffer; + } + + if (sbp->alphabet_code == BLASTAA_SEQ_CODE) + { + sbp->ambiguous_res[sbp->ambig_occupy] = + AMINOACID_TO_NCBISTDAA[toupper((unsigned char) ambiguous_res)]; + } + else { + if (sbp->alphabet_code == BLASTNA_SEQ_CODE) + sbp->ambiguous_res[sbp->ambig_occupy] = + IUPACNA_TO_BLASTNA[toupper((unsigned char) ambiguous_res)]; + else if (sbp->alphabet_code == NCBI4NA_SEQ_CODE) + sbp->ambiguous_res[sbp->ambig_occupy] = + IUPACNA_TO_NCBI4NA[toupper((unsigned char) ambiguous_res)]; + } + (sbp->ambig_occupy)++; + + + return 0; +} + + + +Int2 +BlastScoringOptionsNew(EBlastProgramType program_number, BlastScoringOptions* *options) +{ +*options = (BlastScoringOptions*) calloc(1, sizeof(BlastScoringOptions)); + +if (*options == NULL) +return BLASTERR_INVALIDPARAM; + +if (/*program_number != eBlastTypeBlastn && + program_number != eBlastTypePhiBlastn*/ +!Blast_ProgramIsNucleotide(program_number)) {/*protein-protein options.*/ +(*options)->shift_pen = INT2_MAX; +(*options)->is_ooframe = FALSE; +(*options)->gap_open = BLAST_GAP_OPEN_PROT; +(*options)->gap_extend = BLAST_GAP_EXTN_PROT; +(*options)->matrix = strdup(BLAST_DEFAULT_MATRIX); +} else { /* nucleotide-nucleotide options. */ +(*options)->penalty = BLAST_PENALTY; +(*options)->reward = BLAST_REWARD; +/* This is correct except when greedy extension is used. In that case + these values would have to be reset. */ +(*options)->gap_open = BLAST_GAP_OPEN_NUCL; +(*options)->gap_extend = BLAST_GAP_EXTN_NUCL; +} +if (program_number != eBlastTypeTblastx) { +(*options)->gapped_calculation = TRUE; +} +(*options)->program_number = program_number; +/* By default cross_match-like complexity adjusted scoring is + turned off. RMBlastN is currently the only program to use this. -RMH */ +(*options)->complexity_adjusted_scoring = FALSE; + +return 0; +} + + + +static double +NlmKarlinLambdaNR(double* probs, Int4 d, Int4 low, Int4 high, double lambda0, + double tolx, Int4 itmax, Int4 maxNewton, Int4 * itn ) +{ + Int4 k; + double x0, x, a = 0, b = 1; + double f = 4; /* Larger than any possible value of the poly in [0,1] */ + Int4 isNewton = 0; /* we haven't yet taken a Newton step. */ + + assert( d > 0 ); + + x0 = exp( -lambda0 ); + x = ( 0 < x0 && x0 < 1 ) ? x0 : .5; + + for( k = 0; k < itmax; k++ ) { /* all iteration indices k */ + Int4 i; + double g, fold = f; + Int4 wasNewton = isNewton; /* If true, then the previous step was a */ + /* Newton step */ + isNewton = 0; /* Assume that this step is not */ + + /* Horner's rule for evaluating a polynomial and its derivative */ + g = 0; + f = probs[low]; + for( i = low + d; i < 0; i += d ) { + g = x * g + f; + f = f * x + probs[i]; + } + g = x * g + f; + f = f * x + probs[0] - 1; + for( i = d; i <= high; i += d ) { + g = x * g + f; + f = f * x + probs[i]; + } + /* End Horner's rule */ + + if( f > 0 ) { + a = x; /* move the left endpoint */ + } else if( f < 0 ) { + b = x; /* move the right endpoint */ + } else { /* f == 0 */ + break; /* x is an exact solution */ + } + if( b - a < 2 * a * ( 1 - b ) * tolx ) { + /* The midpoint of the interval converged */ + x = (a + b) / 2; break; + } + + if( k >= maxNewton || + /* If convergence of Newton's method appears to be failing; or */ + ( wasNewton && fabs( f ) > .9 * fabs(fold) ) || + /* if the previous iteration was a Newton step but didn't decrease + * f sufficiently; or */ + g >= 0 + /* if a Newton step will move us away from the desired solution */ + ) { /* then */ + /* bisect */ + x = (a + b)/2; + } else { + /* try a Newton step */ + double p = - f/g; + double y = x + p; + if( y <= a || y >= b ) { /* The proposed iterate is not in (a,b) */ + x = (a + b)/2; + } else { /* The proposed iterate is in (a,b). Accept it. */ + isNewton = 1; + x = y; + if( fabs( p ) < tolx * x * (1-x) ) break; /* Converged */ + } /* else the proposed iterate is in (a,b) */ + } /* else try a Newton step. */ + } /* end for all iteration indices k */ + *itn = k; + return -log(x)/d; +} + +double +Blast_KarlinLambdaNR(Blast_ScoreFreq* sfp, double initialLambdaGuess) +{ + Int4 low; /* Lowest score (must be negative) */ + Int4 high; /* Highest score (must be positive) */ + Int4 itn; + Int4 i, d; + double* sprob; + double returnValue; + + low = sfp->obs_min; + high = sfp->obs_max; + if (sfp->score_avg >= 0.) { /* Expected score must be negative */ + return -1.0; + } + if (BlastScoreChk(low, high) != 0) return -1.; + + sprob = sfp->sprob; + /* Find greatest common divisor of all scores */ + for (i = 1, d = -low; i <= high-low && d > 1; ++i) { + if (sprob[i+low] != 0.0) { + d = BLAST_Gcd(d, i); + } + } + returnValue = + NlmKarlinLambdaNR( sprob, d, low, high, + initialLambdaGuess, + BLAST_KARLIN_LAMBDA_ACCURACY_DEFAULT, + 20, 20 + BLAST_KARLIN_LAMBDA_ITER_DEFAULT, &itn ); + + + return returnValue; +} + +Int2 +Blast_KarlinBlkUngappedCalc(Blast_KarlinBlk* kbp, Blast_ScoreFreq* sfp) +{ + + + if (kbp == NULL || sfp == NULL) + return 1; + + /* Calculate the parameter Lambda */ + + kbp->Lambda = Blast_KarlinLambdaNR(sfp, BLAST_KARLIN_LAMBDA0_DEFAULT); + if (kbp->Lambda < 0.) + goto ErrExit; + + + /* Calculate H */ + + kbp->H = BlastKarlinLtoH(sfp, kbp->Lambda); + if (kbp->H < 0.) + goto ErrExit; + + + /* Calculate K and log(K) */ + + kbp->K = BlastKarlinLHtoK(sfp, kbp->Lambda, kbp->H); + if (kbp->K < 0.) + goto ErrExit; + kbp->logK = log(kbp->K); + + /* Normal return */ + return 0; + + ErrExit: + kbp->Lambda = kbp->H = kbp->K = -1.; + kbp->logK = HUGE_VAL; + return 1; +} + + + +Blast_ScoreFreq* +Blast_ScoreFreqNew(Int4 score_min, Int4 score_max) +{ + Blast_ScoreFreq* sfp; + Int4 range; + + if (BlastScoreChk(score_min, score_max) != 0) + return NULL; + + sfp = (Blast_ScoreFreq*) calloc(1, sizeof(Blast_ScoreFreq)); + if (sfp == NULL) + return NULL; + + range = score_max - score_min + 1; + sfp->sprob = (double*) calloc(range, sizeof(double)); + if (sfp->sprob == NULL) + { + Blast_ScoreFreqFree(sfp); + return NULL; + } + + sfp->sprob0 = sfp->sprob; + sfp->sprob -= score_min; /* center around 0 */ + sfp->score_min = score_min; + sfp->score_max = score_max; + sfp->obs_min = sfp->obs_max = 0; + sfp->score_avg = 0.0; + return sfp; +} + + +static Int2 +Blast_ResFreqNormalize(const BlastScoreBlk* sbp, Blast_ResFreq* rfp, double norm) +{ + Int2 alphabet_stop, index; + double sum = 0., p; + + if (norm == 0.) + return 1; + + alphabet_stop = sbp->alphabet_start + sbp->alphabet_size; + for (index=sbp->alphabet_start; indexprob[index]; + if (p < 0.) + return 1; + sum += p; + } + if (sum <= 0.) + return 0; + + for (index=sbp->alphabet_start; indexprob[index] /= sum; + rfp->prob[index] *= norm; + } + return 0; +} + + +Int2 +Blast_ResFreqStdComp(const BlastScoreBlk* sbp, Blast_ResFreq* rfp) +{ + Uint4 index; + + for (index=0; indexprob[index] = nt_prob[index].p; + } + + + + Blast_ResFreqNormalize(sbp, rfp, 1.0); + + return 0; +} + + + +const int NCBI4NA_TO_BLASTNA[BLASTNA_SIZE] = { + 15, /* Gap, 0 */ + 0, /* A, 1 */ + 1, /* C, 2 */ + 6, /* M, 3 */ + 2, /* G, 4 */ + 4, /* R, 5 */ + 9, /* S, 6 */ + 13, /* V, 7 */ + 3, /* T, 8 */ + 8, /* W, 9 */ + 5, /* Y, 10 */ + 12, /* H, 11 */ + 7, /* K, 12 */ + 11, /* D, 13 */ + 10, /* B, 14 */ + 14 /* N, 15 */ +}; + +const int BLASTNA_TO_NCBI4NA[BLASTNA_SIZE] = { + 1, /* A, 0 */ + 2, /* C, 1 */ + 4, /* G, 2 */ + 8, /* T, 3 */ + 5, /* R, 4 */ + 10, /* Y, 5 */ + 3, /* M, 6 */ + 12, /* K, 7 */ + 9, /* W, 8 */ + 6, /* S, 9 */ + 14, /* B, 10 */ + 13, /* D, 11 */ + 11, /* H, 12 */ + 7, /* V, 13 */ + 15, /* N, 14 */ + 0 /* Gap, 15 */ +}; + +const char BLASTNA_TO_IUPACNA[BLASTNA_SIZE] = { + 'A', 'C', 'G', 'T', 'R', 'Y', 'M', 'K', + 'W', 'S', 'B', 'D', 'H', 'V', 'N', '-' +}; + +const char NCBI4NA_TO_IUPACNA[BLASTNA_SIZE] = { + '-', 'A', 'C', 'M', 'G', 'R', 'S', 'V', + 'T', 'W', 'Y', 'H', 'K', 'D', 'B', 'N' +}; + +const int IUPACNA_TO_BLASTNA[128]={ + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15, 0,10, 1,11,15,15, 2,12,15,15, 7,15, 6,14,15, + 15,15, 4, 9, 3,15,13, 8,15, 5,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15}; + +const int IUPACNA_TO_NCBI4NA[128]={ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1,14, 2,13, 0, 0, 4,11, 0, 0,12, 0, 3,15, 0, + 0, 0, 5, 6, 8, 0, 7, 9, 0,10, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +const int AMINOACID_TO_NCBISTDAA[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,25, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,27,10,11,12,13,26, + 14,15,16,17,18,24,19,20,21,22,23, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +const char NCBISTDAA_TO_AMINOACID[BLASTAA_SIZE] = { + '-','A','B','C','D','E','F','G','H','I','K','L','M', + 'N','P','Q','R','S','T','V','W','X','Y','Z','U','*', + 'O', 'J'}; diff --git a/src/lib/blast/boost_erf.h b/src/lib/blast/boost_erf.h new file mode 100644 index 000000000..b0aad9700 --- /dev/null +++ b/src/lib/blast/boost_erf.h @@ -0,0 +1,48 @@ +/* $Id$ + * =========================================================================== + * + * PUBLIC DOMAIN NOTICE + * National Center for Biotechnology Information + * + * This software/database is a "United States Government Work" under the + * terms of the United States Copyright Act. It was written as part of + * the author's official duties as a United States Government employee and + * thus cannot be copyrighted. This software/database is freely available + * to the public for use. The National Library of Medicine and the U.S. + * Government have not placed any restriction on its use or reproduction. + * + * Although all reasonable efforts have been taken to ensure the accuracy + * and reliability of the software and data, the NLM and the U.S. + * Government do not and cannot warrant the performance or results that + * may be obtained by using this software or data. The NLM and the U.S. + * Government disclaim all warranties, express or implied, including + * warranties of performance, merchantability or fitness for any particular + * purpose. + * + * Please cite the author in any work or product based on this material. + * + * =========================================================================== + * + * Author: Greg Boratyn + * + */ + +#ifndef ALGO_BLAST_CORE__BOOST_ERF +#define ALGO_BLAST_CORE__BOOST_ERF + + +#ifdef __cplusplus +extern "C" { +#endif + +/** Error function */ +double Erf(double z); + +/** Complementary error function */ +double ErfC(double z); + +#ifdef __cplusplus +} +#endif + +#endif /* ALGO_BLAST_CORE__BOOST_ERF */ diff --git a/src/lib/blast/matrix_freq_ratios.c b/src/lib/blast/matrix_freq_ratios.c new file mode 100644 index 000000000..08c9d1837 --- /dev/null +++ b/src/lib/blast/matrix_freq_ratios.c @@ -0,0 +1,1754 @@ +/* =========================================================================== + * + * PUBLIC DOMAIN NOTICE + * National Center for Biotechnology Information + * + * This software/database is a "United States Government Work" under the + * terms of the United States Copyright Act. It was written as part of + * the author's official duties as a United States Government employee and + * thus cannot be copyrighted. This software/database is freely available + * to the public for use. The National Library of Medicine and the U.S. + * Government have not placed any restriction on its use or reproduction. + * + * Although all reasonable efforts have been taken to ensure the accuracy + * and reliability of the software and data, the NLM and the U.S. + * Government do not and cannot warrant the performance or results that + * may be obtained by using this software or data. The NLM and the U.S. + * Government disclaim all warranties, express or implied, including + * warranties of performance, merchantability or fitness for any particular + * purpose. + * + * Please cite the author in any work or product based on this material. + * + * =========================================================================== + * + * Author: Christiam Camacho + * + */ + +/** @file matrix_freq_ratios.c + * Definitions for various scoring matrices' frequency ratios. + */ + +#include "matrix_freq_ratios.h" +#include "blast_psi_priv.h" +#include + + +/** Underlying frequency ratios for BLOSUM45 */ +static const double BLOSUM45_FREQRATIOS[BLASTAA_SIZE][BLASTAA_SIZE] = +{{0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00}, + {0.00000000e+00, 2.95043377e+00, 7.34701741e-01, 8.00397827e-01, + 6.88936672e-01, 8.25164920e-01, 5.87357723e-01, 1.08031132e+00, + 6.54086288e-01, 7.46806187e-01, 7.86209397e-01, 7.12370041e-01, + 8.21348665e-01, 7.89043130e-01, 7.08569419e-01, 8.66678731e-01, + 6.99695540e-01, 1.30031418e+00, 1.00058530e+00, 1.00992663e+00, + 5.65442334e-01, 7.50000000e-01, 6.38727873e-01, 8.41025176e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 7.26064996e-01}, + {0.00000000e+00, 7.34701741e-01, 3.25946658e+00, 6.00232275e-01, + 3.59386786e+00, 1.30000108e+00, 4.95670856e-01, 8.85913772e-01, + 1.08769141e+00, 4.96979579e-01, 1.02264352e+00, 4.72210752e-01, + 5.80058073e-01, 2.86239890e+00, 6.85801186e-01, 9.91862879e-01, + 8.29088221e-01, 1.05297301e+00, 9.85207016e-01, 5.20111147e-01, + 3.80841719e-01, 7.50000000e-01, 6.27051284e-01, 1.18227759e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 4.82061098e-01}, + {0.00000000e+00, 8.00397827e-01, 6.00232275e-01, 1.70900872e+01, + 5.32858023e-01, 5.45353890e-01, 6.01657157e-01, 5.56827599e-01, + 4.90637659e-01, 5.42801532e-01, 5.46735291e-01, 6.72663401e-01, + 6.03730225e-01, 6.80232381e-01, 4.11520916e-01, 4.86216592e-01, + 4.71814444e-01, 7.96941950e-01, 8.21766666e-01, 7.14771712e-01, + 3.33596922e-01, 7.50000000e-01, 4.88896599e-01, 5.22760621e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 6.21018471e-01}, + {0.00000000e+00, 6.88936672e-01, 3.59386786e+00, 5.32858023e-01, + 5.35580571e+00, 1.64256912e+00, 4.31018420e-01, 7.40433725e-01, + 9.76205497e-01, 4.40334375e-01, 9.41681214e-01, 4.62523949e-01, + 4.94422549e-01, 1.50174498e+00, 7.24041930e-01, 9.57667486e-01, + 7.71179788e-01, 9.29413105e-01, 8.76125496e-01, 4.93585385e-01, + 3.73386452e-01, 7.50000000e-01, 6.44927232e-01, 1.38090402e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 4.53699350e-01}, + {0.00000000e+00, 8.25164920e-01, 1.30000108e+00, 5.45353890e-01, + 1.64256912e+00, 3.87327599e+00, 4.97795679e-01, 5.76408577e-01, + 9.61718230e-01, 4.85270933e-01, 1.27686825e+00, 5.70784073e-01, + 6.14865688e-01, 8.93236200e-01, 9.10746443e-01, 1.53097375e+00, + 1.01074883e+00, 9.12113764e-01, 8.32873235e-01, 5.55160304e-01, + 5.19337483e-01, 7.50000000e-01, 6.16552718e-01, 2.97840479e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.36776244e-01}, + {0.00000000e+00, 5.87357723e-01, 4.95670856e-01, 6.01657157e-01, + 4.31018420e-01, 4.97795679e-01, 5.74817622e+00, 4.80346092e-01, + 6.79103477e-01, 1.06375667e+00, 5.29188869e-01, 1.30295859e+00, + 1.06255416e+00, 5.72439082e-01, 4.51176385e-01, 4.43558969e-01, + 5.89537777e-01, 6.09495403e-01, 7.16103747e-01, 9.52475503e-01, + 1.35493791e+00, 7.50000000e-01, 2.18456832e+00, 4.77074668e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 1.20783008e+00}, + {0.00000000e+00, 1.08031132e+00, 8.85913772e-01, 5.56827599e-01, + 7.40433725e-01, 5.76408577e-01, 4.80346092e-01, 5.07068525e+00, + 6.62087167e-01, 4.16341047e-01, 6.77879412e-01, 4.50091586e-01, + 5.84692214e-01, 1.05865660e+00, 7.02165561e-01, 6.87007231e-01, + 5.70228460e-01, 1.05800656e+00, 6.92819008e-01, 4.79214700e-01, + 5.91296285e-01, 7.50000000e-01, 5.49197180e-01, 6.18662539e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 4.36669292e-01}, + {0.00000000e+00, 6.54086288e-01, 1.08769141e+00, 4.90637659e-01, + 9.76205497e-01, 9.61718230e-01, 6.79103477e-01, 6.62087167e-01, + 9.51252809e+00, 4.53313059e-01, 8.90272071e-01, 6.69868446e-01, + 9.18088604e-01, 1.22006964e+00, 6.61223470e-01, 1.15049417e+00, + 9.73045615e-01, 8.54331847e-01, 7.06245757e-01, 4.56693295e-01, + 4.51816356e-01, 7.50000000e-01, 1.47204221e+00, 1.03383965e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.83746261e-01}, + {0.00000000e+00, 7.46806187e-01, 4.96979579e-01, 5.42801532e-01, + 4.40334375e-01, 4.85270933e-01, 1.06375667e+00, 4.16341047e-01, + 4.53313059e-01, 3.23256769e+00, 5.32316397e-01, 1.59618413e+00, + 1.45527106e+00, 5.64240025e-01, 6.09639867e-01, 5.77938325e-01, + 4.88387978e-01, 6.18410187e-01, 8.47505386e-01, 2.17596400e+00, + 5.64907506e-01, 7.50000000e-01, 9.06458192e-01, 5.20674297e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 2.24695958e+00}, + {0.00000000e+00, 7.86209397e-01, 1.02264352e+00, 5.46735291e-01, + 9.41681214e-01, 1.27686825e+00, 5.29188869e-01, 6.77879412e-01, + 8.90272071e-01, 5.32316397e-01, 3.32707189e+00, 5.53563636e-01, + 7.37955763e-01, 1.11877807e+00, 7.81202774e-01, 1.33004839e+00, + 1.94261316e+00, 8.89937552e-01, 8.84562104e-01, 5.91651856e-01, + 5.61572955e-01, 7.50000000e-01, 7.37107274e-01, 1.29718560e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.45113795e-01}, + {0.00000000e+00, 7.12370041e-01, 4.72210752e-01, 6.72663401e-01, + 4.62523949e-01, 5.70784073e-01, 1.30295859e+00, 4.50091586e-01, + 6.69868446e-01, 1.59618413e+00, 5.53563636e-01, 2.99708655e+00, + 1.73144954e+00, 4.83712850e-01, 4.77913692e-01, 6.42028706e-01, + 6.01135200e-01, 5.55659969e-01, 7.80723755e-01, 1.33363845e+00, + 6.70858407e-01, 7.50000000e-01, 9.65090110e-01, 5.98002922e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 2.43995990e+00}, + {0.00000000e+00, 8.21348665e-01, 5.80058073e-01, 6.03730225e-01, + 4.94422549e-01, 6.14865688e-01, 1.06255416e+00, 5.84692214e-01, + 9.18088604e-01, 1.45527106e+00, 7.37955763e-01, 1.73144954e+00, + 4.11411354e+00, 6.81741591e-01, 6.43682874e-01, 9.40467390e-01, + 7.75906233e-01, 6.60370266e-01, 8.60449567e-01, 1.23582796e+00, + 6.34345311e-01, 7.50000000e-01, 1.02316322e+00, 7.39261071e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 1.62161577e+00}, + {0.00000000e+00, 7.89043130e-01, 2.86239890e+00, 6.80232381e-01, + 1.50174498e+00, 8.93236200e-01, 5.72439082e-01, 1.05865660e+00, + 1.22006964e+00, 5.64240025e-01, 1.11877807e+00, 4.83712850e-01, + 6.81741591e-01, 4.47803773e+00, 6.40394172e-01, 1.03246645e+00, + 8.97848625e-01, 1.19968790e+00, 1.11473028e+00, 5.51607804e-01, + 3.89694095e-01, 7.50000000e-01, 6.05825405e-01, 9.46428796e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.15737804e-01}, + {0.00000000e+00, 7.08569419e-01, 6.85801186e-01, 4.11520916e-01, + 7.24041930e-01, 9.10746443e-01, 4.51176385e-01, 7.02165561e-01, + 6.61223470e-01, 6.09639867e-01, 7.81202774e-01, 4.77913692e-01, + 6.43682874e-01, 6.40394172e-01, 8.81911509e+00, 7.15515810e-01, + 5.81631739e-01, 7.49733904e-01, 8.56242933e-01, 5.40037335e-01, + 5.25005050e-01, 7.50000000e-01, 4.78832406e-01, 8.36159027e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.30300041e-01}, + {0.00000000e+00, 8.66678731e-01, 9.91862879e-01, 4.86216592e-01, + 9.57667486e-01, 1.53097375e+00, 4.43558969e-01, 6.87007231e-01, + 1.15049417e+00, 5.77938325e-01, 1.33004839e+00, 6.42028706e-01, + 9.40467390e-01, 1.03246645e+00, 7.15515810e-01, 4.40728842e+00, + 1.32912854e+00, 1.09183956e+00, 7.80601862e-01, 5.47266398e-01, + 6.45177884e-01, 7.50000000e-01, 8.29182983e-01, 2.62986317e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 6.16540522e-01}, + {0.00000000e+00, 6.99695540e-01, 8.29088221e-01, 4.71814444e-01, + 7.71179788e-01, 1.01074883e+00, 5.89537777e-01, 5.70228460e-01, + 9.73045615e-01, 4.88387978e-01, 1.94261316e+00, 6.01135200e-01, + 7.75906233e-01, 8.97848625e-01, 5.81631739e-01, 1.32912854e+00, + 4.74702063e+00, 7.99048209e-01, 7.15164318e-01, 5.77699501e-01, + 5.80165842e-01, 7.50000000e-01, 8.07446927e-01, 1.13238507e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.56296615e-01}, + {0.00000000e+00, 1.30031418e+00, 1.05297301e+00, 7.96941950e-01, + 9.29413105e-01, 9.12113764e-01, 6.09495403e-01, 1.05800656e+00, + 8.54331847e-01, 6.18410187e-01, 8.89937552e-01, 5.55659969e-01, + 6.60370266e-01, 1.19968790e+00, 7.49733904e-01, 1.09183956e+00, + 7.99048209e-01, 2.78188630e+00, 1.47248598e+00, 7.27836330e-01, + 4.28363793e-01, 7.50000000e-01, 7.05878947e-01, 9.80777594e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.80615182e-01}, + {0.00000000e+00, 1.00058530e+00, 9.85207016e-01, 8.21766666e-01, + 8.76125496e-01, 8.32873235e-01, 7.16103747e-01, 6.92819008e-01, + 7.06245757e-01, 8.47505386e-01, 8.84562104e-01, 7.80723755e-01, + 8.60449567e-01, 1.11473028e+00, 8.56242933e-01, 7.80601862e-01, + 7.15164318e-01, 1.47248598e+00, 3.13871529e+00, 1.04019697e+00, + 4.54128072e-01, 7.50000000e-01, 7.43457494e-01, 8.12903077e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 8.07282226e-01}, + {0.00000000e+00, 1.00992663e+00, 5.20111147e-01, 7.14771712e-01, + 4.93585385e-01, 5.55160304e-01, 9.52475503e-01, 4.79214700e-01, + 4.56693295e-01, 2.17596400e+00, 5.91651856e-01, 1.33363845e+00, + 1.23582796e+00, 5.51607804e-01, 5.40037335e-01, 5.47266398e-01, + 5.77699501e-01, 7.27836330e-01, 1.04019697e+00, 2.87075890e+00, + 4.73320057e-01, 7.50000000e-01, 8.09252575e-01, 5.52144455e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 1.66862396e+00}, + {0.00000000e+00, 5.65442334e-01, 3.80841719e-01, 3.33596922e-01, + 3.73386452e-01, 5.19337483e-01, 1.35493791e+00, 5.91296285e-01, + 4.51816356e-01, 5.64907506e-01, 5.61572955e-01, 6.70858407e-01, + 6.34345311e-01, 3.89694095e-01, 5.25005050e-01, 6.45177884e-01, + 5.80165842e-01, 4.28363793e-01, 4.54128072e-01, 4.73320057e-01, + 2.97023509e+01, 7.50000000e-01, 1.80096028e+00, 5.67414520e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 6.28722660e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 6.38727873e-01, 6.27051284e-01, 4.88896599e-01, + 6.44927232e-01, 6.16552718e-01, 2.18456832e+00, 5.49197180e-01, + 1.47204221e+00, 9.06458192e-01, 7.37107274e-01, 9.65090110e-01, + 1.02316322e+00, 6.05825405e-01, 4.78832406e-01, 8.29182983e-01, + 8.07446927e-01, 7.05878947e-01, 7.43457494e-01, 8.09252575e-01, + 1.80096028e+00, 7.50000000e-01, 5.75351902e+00, 6.97787623e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 9.41772708e-01}, + {0.00000000e+00, 8.41025176e-01, 1.18227759e+00, 5.22760621e-01, + 1.38090402e+00, 2.97840479e+00, 4.77074668e-01, 6.18662539e-01, + 1.03383965e+00, 5.20674297e-01, 1.29718560e+00, 5.98002922e-01, + 7.39261071e-01, 9.46428796e-01, 8.36159027e-01, 2.62986317e+00, + 1.13238507e+00, 9.80777594e-01, 8.12903077e-01, 5.52144455e-01, + 5.67414520e-01, 7.50000000e-01, 6.97787623e-01, 2.84524527e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.67250003e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 1.33300000e+00, 2.90000000e-01, 2.90000000e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 7.26064996e-01, 4.82061098e-01, 6.21018471e-01, + 4.53699350e-01, 5.36776244e-01, 1.20783008e+00, 4.36669292e-01, + 5.83746261e-01, 2.24695958e+00, 5.45113795e-01, 2.43995990e+00, + 1.62161577e+00, 5.15737804e-01, 5.30300041e-01, 6.16540522e-01, + 5.56296615e-01, 5.80615182e-01, 8.07282226e-01, 1.66862396e+00, + 6.28722660e-01, 7.50000000e-01, 9.41772708e-01, 5.67250003e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 2.36320536e+00}}; + + +/** Underlying frequency ratios for BLOSUM50 */ +static const double BLOSUM50_FREQRATIOS[BLASTAA_SIZE][BLASTAA_SIZE] = +{{0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00}, + {0.00000000e+00, 3.27354473e+00, 6.87168642e-01, 8.87624875e-01, + 6.59704230e-01, 7.97359654e-01, 5.46298170e-01, 1.10130683e+00, + 6.41220589e-01, 7.15157692e-01, 7.47622201e-01, 6.56954186e-01, + 8.53472686e-01, 7.19514908e-01, 7.14770771e-01, 8.19913708e-01, + 6.68351460e-01, 1.36359270e+00, 9.67331593e-01, 9.81625416e-01, + 4.63560176e-01, 7.50000000e-01, 5.96400452e-01, 8.05980349e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 6.80310043e-01}, + {0.00000000e+00, 6.87168642e-01, 3.67565906e+00, 5.07294595e-01, + 4.02052534e+00, 1.31892999e+00, 3.83849184e-01, 8.47577177e-01, + 1.11810068e+00, 4.07212168e-01, 9.68440788e-01, 3.91166888e-01, + 5.16058658e-01, 3.26949213e+00, 6.61247189e-01, 9.98204754e-01, + 7.59007679e-01, 1.06055276e+00, 9.55438618e-01, 4.46192804e-01, + 3.38571955e-01, 7.50000000e-01, 5.55905269e-01, 1.19634119e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 3.97605526e-01}, + {0.00000000e+00, 8.87624875e-01, 5.07294595e-01, 1.82308935e+01, + 4.27724382e-01, 4.56030174e-01, 5.64985221e-01, 5.24350848e-01, + 5.17412429e-01, 5.87186086e-01, 4.59864212e-01, 6.50074165e-01, + 6.80946766e-01, 6.01008569e-01, 4.03060607e-01, 4.81296027e-01, + 4.27834290e-01, 8.29850973e-01, 8.17890869e-01, 8.08665030e-01, + 3.12131245e-01, 7.50000000e-01, 5.38704705e-01, 4.65687383e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 6.24838486e-01}, + {0.00000000e+00, 6.59704230e-01, 4.02052534e+00, 4.27724382e-01, + 6.11147890e+00, 1.65784569e+00, 3.37276799e-01, 7.44468416e-01, + 8.83789762e-01, 3.70146565e-01, 8.90348134e-01, 3.72923686e-01, + 4.35009775e-01, 1.55790069e+00, 7.09628659e-01, 9.66997497e-01, + 6.78533894e-01, 9.21480865e-01, 8.25179634e-01, 4.31712984e-01, + 3.07050170e-01, 7.50000000e-01, 5.04473723e-01, 1.39378711e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 3.71809285e-01}, + {0.00000000e+00, 7.97359654e-01, 1.31892999e+00, 4.56030174e-01, + 1.65784569e+00, 4.43735647e+00, 4.56055858e-01, 5.39909105e-01, + 9.11487897e-01, 4.15558851e-01, 1.33108575e+00, 4.78428941e-01, + 6.05096970e-01, 9.19771370e-01, 8.38805682e-01, 1.67134451e+00, + 9.76357610e-01, 8.82100970e-01, 8.19031320e-01, 4.79075838e-01, + 5.48039829e-01, 7.50000000e-01, 6.51686488e-01, 3.38012103e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 4.53200481e-01}, + {0.00000000e+00, 5.46298170e-01, 3.83849184e-01, 5.64985221e-01, + 3.37276799e-01, 4.56055858e-01, 6.63625360e+00, 4.13949535e-01, + 7.54714659e-01, 9.89742646e-01, 4.44979578e-01, 1.26171801e+00, + 1.05158910e+00, 4.38699901e-01, 3.75736079e-01, 4.16967765e-01, + 4.61789222e-01, 5.52981315e-01, 5.70349305e-01, 8.59643167e-01, + 1.34807169e+00, 7.50000000e-01, 2.42442622e+00, 4.41115461e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 1.15257995e+00}, + {0.00000000e+00, 1.10130683e+00, 8.47577177e-01, 5.24350848e-01, + 7.44468416e-01, 5.39909105e-01, 4.13949535e-01, 5.79218671e+00, + 6.01271290e-01, 3.70370366e-01, 6.54870319e-01, 3.76903001e-01, + 5.19438610e-01, 9.69013722e-01, 6.20168128e-01, 6.41416113e-01, + 5.16985831e-01, 9.99248542e-01, 6.36269018e-01, 4.02720033e-01, + 5.04888636e-01, 7.50000000e-01, 4.67274430e-01, 5.78707493e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 3.74281590e-01}, + {0.00000000e+00, 6.41220589e-01, 1.11810068e+00, 5.17412429e-01, + 8.83789762e-01, 9.11487897e-01, 7.54714659e-01, 6.01271290e-01, + 1.04489376e+01, 4.11545408e-01, 9.45545516e-01, 5.47445792e-01, + 7.60124356e-01, 1.39406083e+00, 5.81906417e-01, 1.20911332e+00, + 9.81604707e-01, 8.22540644e-01, 6.52641826e-01, 4.13620259e-01, + 4.75002356e-01, 7.50000000e-01, 1.57000854e+00, 1.02524740e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 4.92911793e-01}, + {0.00000000e+00, 7.15157692e-01, 4.07212168e-01, 5.87186086e-01, + 3.70146565e-01, 4.15558851e-01, 9.89742646e-01, 3.70370366e-01, + 4.11545408e-01, 3.41093885e+00, 4.68297844e-01, 1.69677965e+00, + 1.43810563e+00, 4.50866254e-01, 5.11210841e-01, 5.02656404e-01, + 4.35318230e-01, 5.45643330e-01, 8.60722536e-01, 2.31272269e+00, + 5.22495607e-01, 7.50000000e-01, 8.26095669e-01, 4.48849604e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 2.38463611e+00}, + {0.00000000e+00, 7.47622201e-01, 9.68440788e-01, 4.59864212e-01, + 8.90348134e-01, 1.33108575e+00, 4.44979578e-01, 6.54870319e-01, + 9.45545516e-01, 4.68297844e-01, 3.88090096e+00, 4.79194854e-01, + 6.85231759e-01, 1.06041456e+00, 7.63679086e-01, 1.41855717e+00, + 2.06468049e+00, 8.92977250e-01, 8.44796364e-01, 5.22802406e-01, + 4.61593643e-01, 7.50000000e-01, 6.67754286e-01, 1.36451940e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 4.74822110e-01}, + {0.00000000e+00, 6.56954186e-01, 3.91166888e-01, 6.50074165e-01, + 3.72923686e-01, 4.78428941e-01, 1.26171801e+00, 3.76903001e-01, + 5.47445792e-01, 1.69677965e+00, 4.79194854e-01, 3.32815910e+00, + 1.78991633e+00, 4.12652855e-01, 4.43674459e-01, 5.62937315e-01, + 5.54029894e-01, 5.10605044e-01, 7.59171671e-01, 1.32427289e+00, + 6.01518374e-01, 7.50000000e-01, 8.58373419e-01, 5.10730048e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 2.67352044e+00}, + {0.00000000e+00, 8.53472686e-01, 5.16058658e-01, 6.80946766e-01, + 4.35009775e-01, 6.05096970e-01, 1.05158910e+00, 5.19438610e-01, + 7.60124356e-01, 1.43810563e+00, 6.85231759e-01, 1.78991633e+00, + 4.81561797e+00, 6.11514139e-01, 5.44813932e-01, 9.59915799e-01, + 6.78015272e-01, 6.82291911e-01, 8.82308943e-01, 1.21329921e+00, + 7.61522900e-01, 7.50000000e-01, 9.17831903e-01, 7.40717150e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 1.64874201e+00}, + {0.00000000e+00, 7.19514908e-01, 3.26949213e+00, 6.01008569e-01, + 1.55790069e+00, 9.19771370e-01, 4.38699901e-01, 9.69013722e-01, + 1.39406083e+00, 4.50866254e-01, 1.06041456e+00, 4.12652855e-01, + 6.11514139e-01, 5.28532229e+00, 6.04265819e-01, 1.03495916e+00, + 8.53785837e-01, 1.22434496e+00, 1.10885139e+00, 4.63246441e-01, + 3.75696800e-01, 7.50000000e-01, 6.16478872e-01, 9.63798879e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 4.27987098e-01}, + {0.00000000e+00, 7.14770771e-01, 6.61247189e-01, 4.03060607e-01, + 7.09628659e-01, 8.38805682e-01, 3.75736079e-01, 6.20168128e-01, + 5.81906417e-01, 5.11210841e-01, 7.63679086e-01, 4.43674459e-01, + 5.44813932e-01, 6.04265819e-01, 1.02035160e+01, 7.50499600e-01, + 5.18638945e-01, 7.59438841e-01, 7.48565360e-01, 5.24438149e-01, + 4.20092966e-01, 7.50000000e-01, 4.68173126e-01, 8.05053001e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 4.70775405e-01}, + {0.00000000e+00, 8.19913708e-01, 9.98204754e-01, 4.81296027e-01, + 9.66997497e-01, 1.67134451e+00, 4.16967765e-01, 6.41416113e-01, + 1.20911332e+00, 5.02656404e-01, 1.41855717e+00, 5.62937315e-01, + 9.59915799e-01, 1.03495916e+00, 7.50499600e-01, 4.69722165e+00, + 1.35733364e+00, 1.06872445e+00, 8.10316946e-01, 5.57384267e-01, + 7.14705591e-01, 7.50000000e-01, 7.42076535e-01, 2.82790659e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.38747839e-01}, + {0.00000000e+00, 6.68351460e-01, 7.59007679e-01, 4.27834290e-01, + 6.78533894e-01, 9.76357610e-01, 4.61789222e-01, 5.16985831e-01, + 9.81604707e-01, 4.35318230e-01, 2.06468049e+00, 5.54029894e-01, + 6.78015272e-01, 8.53785837e-01, 5.18638945e-01, 1.35733364e+00, + 5.37787401e+00, 8.04499038e-01, 7.36510915e-01, 5.12488758e-01, + 5.28823677e-01, 7.50000000e-01, 7.27125062e-01, 1.12197569e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.06393371e-01}, + {0.00000000e+00, 1.36359270e+00, 1.06055276e+00, 8.29850973e-01, + 9.21480865e-01, 8.82100970e-01, 5.52981315e-01, 9.99248542e-01, + 8.22540644e-01, 5.45643330e-01, 8.92977250e-01, 5.10605044e-01, + 6.82291911e-01, 1.22434496e+00, 7.59438841e-01, 1.06872445e+00, + 8.04499038e-01, 3.14298812e+00, 1.49727124e+00, 6.78664919e-01, + 3.92328021e-01, 7.50000000e-01, 6.51019697e-01, 9.53432893e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.24665180e-01}, + {0.00000000e+00, 9.67331593e-01, 9.55438618e-01, 8.17890869e-01, + 8.25179634e-01, 8.19031320e-01, 5.70349305e-01, 6.36269018e-01, + 6.52641826e-01, 8.60722536e-01, 8.44796364e-01, 7.59171671e-01, + 8.82308943e-01, 1.10885139e+00, 7.48565360e-01, 8.10316946e-01, + 7.36510915e-01, 1.49727124e+00, 3.55307500e+00, 1.05992520e+00, + 5.02669810e-01, 7.50000000e-01, 6.90730891e-01, 8.15700480e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 7.99921922e-01}, + {0.00000000e+00, 9.81625416e-01, 4.46192804e-01, 8.08665030e-01, + 4.31712984e-01, 4.79075838e-01, 8.59643167e-01, 4.02720033e-01, + 4.13620259e-01, 2.31272269e+00, 5.22802406e-01, 1.32427289e+00, + 1.21329921e+00, 4.63246441e-01, 5.24438149e-01, 5.57384267e-01, + 5.12488758e-01, 6.78664919e-01, 1.05992520e+00, 3.11745700e+00, + 4.84839541e-01, 7.50000000e-01, 7.27350506e-01, 5.09007179e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 1.72091725e+00}, + {0.00000000e+00, 4.63560176e-01, 3.38571955e-01, 3.12131245e-01, + 3.07050170e-01, 5.48039829e-01, 1.34807169e+00, 5.04888636e-01, + 4.75002356e-01, 5.22495607e-01, 4.61593643e-01, 6.01518374e-01, + 7.61522900e-01, 3.75696800e-01, 4.20092966e-01, 7.14705591e-01, + 5.28823677e-01, 3.92328021e-01, 5.02669810e-01, 4.84839541e-01, + 3.13609332e+01, 7.50000000e-01, 1.76515899e+00, 6.11743441e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 5.69808181e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 5.96400452e-01, 5.55905269e-01, 5.38704705e-01, + 5.04473723e-01, 6.51686488e-01, 2.42442622e+00, 4.67274430e-01, + 1.57000854e+00, 8.26095669e-01, 6.67754286e-01, 8.58373419e-01, + 9.17831903e-01, 6.16478872e-01, 4.68173126e-01, 7.42076535e-01, + 7.27125062e-01, 6.51019697e-01, 6.90730891e-01, 7.27350506e-01, + 1.76515899e+00, 7.50000000e-01, 6.89283261e+00, 6.86235710e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 8.45421029e-01}, + {0.00000000e+00, 8.05980349e-01, 1.19634119e+00, 4.65687383e-01, + 1.39378711e+00, 3.38012103e+00, 4.41115461e-01, 5.78707493e-01, + 1.02524740e+00, 4.48849604e-01, 1.36451940e+00, 5.10730048e-01, + 7.40717150e-01, 9.63798879e-01, 8.05053001e-01, 2.82790659e+00, + 1.12197569e+00, 9.53432893e-01, 8.15700480e-01, 5.09007179e-01, + 6.11743441e-01, 7.50000000e-01, 6.86235710e-01, 3.16905156e+00, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 4.85898712e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, 2.90000000e-01, + 2.90000000e-01, 1.33300000e+00, 2.90000000e-01, 2.90000000e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 6.80310043e-01, 3.97605526e-01, 6.24838486e-01, + 3.71809285e-01, 4.53200481e-01, 1.15257995e+00, 3.74281590e-01, + 4.92911793e-01, 2.38463611e+00, 4.74822110e-01, 2.67352044e+00, + 1.64874201e+00, 4.27987098e-01, 4.70775405e-01, 5.38747839e-01, + 5.06393371e-01, 5.24665180e-01, 7.99921922e-01, 1.72091725e+00, + 5.69808181e-01, 7.50000000e-01, 8.45421029e-01, 4.85898712e-01, + 7.50000000e-01, 2.90000000e-01, 7.50000000e-01, 2.55759716e+00}}; + + +/** Underlying frequency ratios for BLOSUM62 */ +static const double BLOSUM62_FREQRATIOS[BLASTAA_SIZE][BLASTAA_SIZE] = +{{0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00}, + {0.00000000e+00, 3.90294070e+00, 5.64459671e-01, 8.67987664e-01, + 5.44605275e-01, 7.41264113e-01, 4.64893827e-01, 1.05686961e+00, + 5.69364849e-01, 6.32481035e-01, 7.75390239e-01, 6.01945975e-01, + 7.23150342e-01, 5.88307640e-01, 7.54121369e-01, 7.56803943e-01, + 6.12698600e-01, 1.47210399e+00, 9.84401956e-01, 9.36458396e-01, + 4.16548781e-01, 7.50000000e-01, 5.42611869e-01, 7.47274948e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 6.14377313e-01}, + {0.00000000e+00, 5.64459671e-01, 4.43758048e+00, 3.45226274e-01, + 4.74290926e+00, 1.33503378e+00, 3.24101420e-01, 7.38524318e-01, + 9.25449581e-01, 3.33981361e-01, 8.54849426e-01, 2.97257620e-01, + 4.04640322e-01, 4.07083696e+00, 5.53838329e-01, 9.44103648e-01, + 7.02873767e-01, 1.05798620e+00, 8.26250098e-01, 3.51280513e-01, + 2.52855433e-01, 7.50000000e-01, 4.09444638e-01, 1.18382127e+00, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 3.12208474e-01}, + {0.00000000e+00, 8.67987664e-01, 3.45226274e-01, 1.95765857e+01, + 3.01454345e-01, 2.85934574e-01, 4.38990118e-01, 4.20387870e-01, + 3.55049505e-01, 6.53458801e-01, 3.49128465e-01, 6.42275633e-01, + 6.11354340e-01, 3.97802620e-01, 3.79562691e-01, 3.65781531e-01, + 3.08939296e-01, 7.38415701e-01, 7.40551692e-01, 7.55844055e-01, + 4.49983903e-01, 7.50000000e-01, 4.34203398e-01, 3.16819526e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 6.46828489e-01}, + {0.00000000e+00, 5.44605275e-01, 4.74290926e+00, 3.01454345e-01, + 7.39792738e+00, 1.68781075e+00, 2.98969081e-01, 6.34301019e-01, + 6.78558839e-01, 3.39015407e-01, 7.84090406e-01, 2.86613046e-01, + 3.46454634e-01, 1.55385281e+00, 5.98716826e-01, 8.97081129e-01, + 5.73200024e-01, 9.13504624e-01, 6.94789868e-01, 3.36500142e-01, + 2.32102315e-01, 7.50000000e-01, 3.45683565e-01, 1.38195506e+00, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 3.07946931e-01}, + {0.00000000e+00, 7.41264113e-01, 1.33503378e+00, 2.85934574e-01, + 1.68781075e+00, 5.46952608e+00, 3.30743991e-01, 4.81267655e-01, + 9.60040718e-01, 3.30522558e-01, 1.30827885e+00, 3.72873704e-01, + 5.00342289e-01, 9.11298183e-01, 6.79202587e-01, 1.90173784e+00, + 9.60797602e-01, 9.50357185e-01, 7.41425610e-01, 4.28943130e-01, + 3.74300212e-01, 7.50000000e-01, 4.96467354e-01, 4.08949895e+00, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 3.55631838e-01}, + {0.00000000e+00, 4.64893827e-01, 3.24101420e-01, 4.38990118e-01, + 2.98969081e-01, 3.30743991e-01, 8.12879702e+00, 3.40640908e-01, + 6.51990521e-01, 9.45769883e-01, 3.44043119e-01, 1.15459749e+00, + 1.00437163e+00, 3.54288952e-01, 2.87444758e-01, 3.33972402e-01, + 3.80726330e-01, 4.39973597e-01, 4.81693683e-01, 7.45089738e-01, + 1.37437942e+00, 7.50000000e-01, 2.76938063e+00, 3.31992746e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 1.06958025e+00}, + {0.00000000e+00, 1.05686961e+00, 7.38524318e-01, 4.20387870e-01, + 6.34301019e-01, 4.81267655e-01, 3.40640908e-01, 6.87630691e+00, + 4.92966576e-01, 2.75009722e-01, 5.88871736e-01, 2.84504012e-01, + 3.95486600e-01, 8.63711406e-01, 4.77385507e-01, 5.38649627e-01, + 4.49983999e-01, 9.03596525e-01, 5.79271582e-01, 3.36954912e-01, + 4.21690355e-01, 7.50000000e-01, 3.48714366e-01, 5.03463109e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 2.80638726e-01}, + {0.00000000e+00, 5.69364849e-01, 9.25449581e-01, 3.55049505e-01, + 6.78558839e-01, 9.60040718e-01, 6.51990521e-01, 4.92966576e-01, + 1.35059997e+01, 3.26288125e-01, 7.78887490e-01, 3.80675486e-01, + 5.84132623e-01, 1.22200067e+00, 4.72879831e-01, 1.16798104e+00, + 9.17048021e-01, 7.36731740e-01, 5.57503254e-01, 3.39447442e-01, + 4.44088955e-01, 7.50000000e-01, 1.79790413e+00, 1.04047242e+00, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 3.58533474e-01}, + {0.00000000e+00, 6.32481035e-01, 3.33981361e-01, 6.53458801e-01, + 3.39015407e-01, 3.30522558e-01, 9.45769883e-01, 2.75009722e-01, + 3.26288125e-01, 3.99792994e+00, 3.96372934e-01, 1.69443475e+00, + 1.47774450e+00, 3.27934752e-01, 3.84662860e-01, 3.82937802e-01, + 3.54751311e-01, 4.43163582e-01, 7.79816110e-01, 2.41751209e+00, + 4.08874390e-01, 7.50000000e-01, 6.30388931e-01, 3.50796872e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 2.63222650e+00}, + {0.00000000e+00, 7.75390239e-01, 8.54849426e-01, 3.49128465e-01, + 7.84090406e-01, 1.30827885e+00, 3.44043119e-01, 5.88871736e-01, + 7.78887490e-01, 3.96372934e-01, 4.76433717e+00, 4.28270363e-01, + 6.25302816e-01, 9.39841129e-01, 7.03774479e-01, 1.55432308e+00, + 2.07680867e+00, 9.31919141e-01, 7.92905803e-01, 4.56542720e-01, + 3.58930071e-01, 7.50000000e-01, 5.32179333e-01, 1.40344922e+00, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 4.15284382e-01}, + {0.00000000e+00, 6.01945975e-01, 2.97257620e-01, 6.42275633e-01, + 2.86613046e-01, 3.72873704e-01, 1.15459749e+00, 2.84504012e-01, + 3.80675486e-01, 1.69443475e+00, 4.28270363e-01, 3.79662137e+00, + 1.99429557e+00, 3.10043276e-01, 3.71121724e-01, 4.77325586e-01, + 4.73919278e-01, 4.28893743e-01, 6.60328975e-01, 1.31423573e+00, + 5.68037074e-01, 7.50000000e-01, 6.92059423e-01, 4.13275887e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 2.94078574e+00}, + {0.00000000e+00, 7.23150342e-01, 4.04640322e-01, 6.11354340e-01, + 3.46454634e-01, 5.00342289e-01, 1.00437163e+00, 3.95486600e-01, + 5.84132623e-01, 1.47774450e+00, 6.25302816e-01, 1.99429557e+00, + 6.48145121e+00, 4.74529655e-01, 4.23898024e-01, 8.64250293e-01, + 6.22623369e-01, 5.98558924e-01, 7.93801616e-01, 1.26893679e+00, + 6.10296214e-01, 7.50000000e-01, 7.08364628e-01, 6.41102583e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 1.78399892e+00}, + {0.00000000e+00, 5.88307640e-01, 4.07083696e+00, 3.97802620e-01, + 1.55385281e+00, 9.11298183e-01, 3.54288952e-01, 8.63711406e-01, + 1.22200067e+00, 3.27934752e-01, 9.39841129e-01, 3.10043276e-01, + 4.74529655e-01, 7.09409488e+00, 4.99932836e-01, 1.00058442e+00, + 8.58630478e-01, 1.23152924e+00, 9.84152635e-01, 3.69033853e-01, + 2.77782896e-01, 7.50000000e-01, 4.86030806e-01, 9.45834265e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 3.17327197e-01}, + {0.00000000e+00, 7.54121369e-01, 5.53838329e-01, 3.79562691e-01, + 5.98716826e-01, 6.79202587e-01, 2.87444758e-01, 4.77385507e-01, + 4.72879831e-01, 3.84662860e-01, 7.03774479e-01, 3.71121724e-01, + 4.23898024e-01, 4.99932836e-01, 1.28375437e+01, 6.41280589e-01, + 4.81534905e-01, 7.55503259e-01, 6.88897122e-01, 4.43082984e-01, + 2.81833164e-01, 7.50000000e-01, 3.63521119e-01, 6.64534287e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 3.76634549e-01}, + {0.00000000e+00, 7.56803943e-01, 9.44103648e-01, 3.65781531e-01, + 8.97081129e-01, 1.90173784e+00, 3.33972402e-01, 5.38649627e-01, + 1.16798104e+00, 3.82937802e-01, 1.55432308e+00, 4.77325586e-01, + 8.64250293e-01, 1.00058442e+00, 6.41280589e-01, 6.24442175e+00, + 1.40579606e+00, 9.65555228e-01, 7.91320741e-01, 4.66777931e-01, + 5.09360272e-01, 7.50000000e-01, 6.11094097e-01, 3.58149606e+00, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 4.38898727e-01}, + {0.00000000e+00, 6.12698600e-01, 7.02873767e-01, 3.08939296e-01, + 5.73200024e-01, 9.60797602e-01, 3.80726330e-01, 4.49983999e-01, + 9.17048021e-01, 3.54751311e-01, 2.07680867e+00, 4.73919278e-01, + 6.22623369e-01, 8.58630478e-01, 4.81534905e-01, 1.40579606e+00, + 6.66557707e+00, 7.67165633e-01, 6.77754679e-01, 4.20072316e-01, + 3.95102106e-01, 7.50000000e-01, 5.55965425e-01, 1.13292384e+00, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 4.25403989e-01}, + {0.00000000e+00, 1.47210399e+00, 1.05798620e+00, 7.38415701e-01, + 9.13504624e-01, 9.50357185e-01, 4.39973597e-01, 9.03596525e-01, + 7.36731740e-01, 4.43163582e-01, 9.31919141e-01, 4.28893743e-01, + 5.98558924e-01, 1.23152924e+00, 7.55503259e-01, 9.65555228e-01, + 7.67165633e-01, 3.84284741e+00, 1.61392097e+00, 5.65223766e-01, + 3.85303035e-01, 7.50000000e-01, 5.57520051e-01, 9.56235816e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 4.34703235e-01}, + {0.00000000e+00, 9.84401956e-01, 8.26250098e-01, 7.40551692e-01, + 6.94789868e-01, 7.41425610e-01, 4.81693683e-01, 5.79271582e-01, + 5.57503254e-01, 7.79816110e-01, 7.92905803e-01, 6.60328975e-01, + 7.93801616e-01, 9.84152635e-01, 6.88897122e-01, 7.91320741e-01, + 6.77754679e-01, 1.61392097e+00, 4.83210516e+00, 9.80943005e-01, + 4.30934144e-01, 7.50000000e-01, 5.73156574e-01, 7.60725140e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 7.08974203e-01}, + {0.00000000e+00, 9.36458396e-01, 3.51280513e-01, 7.55844055e-01, + 3.36500142e-01, 4.28943130e-01, 7.45089738e-01, 3.36954912e-01, + 3.39447442e-01, 2.41751209e+00, 4.56542720e-01, 1.31423573e+00, + 1.26893679e+00, 3.69033853e-01, 4.43082984e-01, 4.66777931e-01, + 4.20072316e-01, 5.65223766e-01, 9.80943005e-01, 3.69215640e+00, + 3.74456332e-01, 7.50000000e-01, 6.58038693e-01, 4.43577702e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 1.76339815e+00}, + {0.00000000e+00, 4.16548781e-01, 2.52855433e-01, 4.49983903e-01, + 2.32102315e-01, 3.74300212e-01, 1.37437942e+00, 4.21690355e-01, + 4.44088955e-01, 4.08874390e-01, 3.58930071e-01, 5.68037074e-01, + 6.10296214e-01, 2.77782896e-01, 2.81833164e-01, 5.09360272e-01, + 3.95102106e-01, 3.85303035e-01, 4.30934144e-01, 3.74456332e-01, + 3.81077833e+01, 7.50000000e-01, 2.10980812e+00, 4.26541694e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 5.03239261e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 5.42611869e-01, 4.09444638e-01, 4.34203398e-01, + 3.45683565e-01, 4.96467354e-01, 2.76938063e+00, 3.48714366e-01, + 1.79790413e+00, 6.30388931e-01, 5.32179333e-01, 6.92059423e-01, + 7.08364628e-01, 4.86030806e-01, 3.63521119e-01, 6.11094097e-01, + 5.55965425e-01, 5.57520051e-01, 5.73156574e-01, 6.58038693e-01, + 2.10980812e+00, 7.50000000e-01, 9.83220341e+00, 5.40805192e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 6.66952325e-01}, + {0.00000000e+00, 7.47274948e-01, 1.18382127e+00, 3.16819526e-01, + 1.38195506e+00, 4.08949895e+00, 3.31992746e-01, 5.03463109e-01, + 1.04047242e+00, 3.50796872e-01, 1.40344922e+00, 4.13275887e-01, + 6.41102583e-01, 9.45834265e-01, 6.64534287e-01, 3.58149606e+00, + 1.13292384e+00, 9.56235816e-01, 7.60725140e-01, 4.43577702e-01, + 4.26541694e-01, 7.50000000e-01, 5.40805192e-01, 3.89300249e+00, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 3.87839626e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 2.50000000e-01, 2.50000000e-01, 2.50000000e-01, + 2.50000000e-01, 2.50000000e-01, 2.50000000e-01, 2.50000000e-01, + 2.50000000e-01, 2.50000000e-01, 2.50000000e-01, 2.50000000e-01, + 2.50000000e-01, 2.50000000e-01, 2.50000000e-01, 2.50000000e-01, + 2.50000000e-01, 2.50000000e-01, 2.50000000e-01, 2.50000000e-01, + 2.50000000e-01, 2.50000000e-01, 2.50000000e-01, 2.50000000e-01, + 2.50000000e-01, 1.33300000e+00, 2.50000000e-01, 2.50000000e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 6.14377313e-01, 3.12208474e-01, 6.46828489e-01, + 3.07946931e-01, 3.55631838e-01, 1.06958025e+00, 2.80638726e-01, + 3.58533474e-01, 2.63222650e+00, 4.15284382e-01, 2.94078574e+00, + 1.78399892e+00, 3.17327197e-01, 3.76634549e-01, 4.38898727e-01, + 4.25403989e-01, 4.34703235e-01, 7.08974203e-01, 1.76339815e+00, + 5.03239261e-01, 7.50000000e-01, 6.66952325e-01, 3.87839626e-01, + 7.50000000e-01, 2.50000000e-01, 7.50000000e-01, 2.81516607e+00}}; + + +/** Underlying frequency ratios for BLOSUM80 */ +static const double BLOSUM80_FREQRATIOS[BLASTAA_SIZE][BLASTAA_SIZE] = +{{0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00}, + {0.00000000e+00, 4.77313697e+00, 4.77250219e-01, 7.31790935e-01, + 4.50905428e-01, 7.03141318e-01, 3.96805472e-01, 9.57108118e-01, + 5.14428496e-01, 5.43302985e-01, 7.22895459e-01, 5.04576848e-01, + 6.25509899e-01, 5.10109254e-01, 7.71095354e-01, 6.95763501e-01, + 5.55324683e-01, 1.53463504e+00, 9.79634362e-01, 8.65874830e-01, + 3.09281009e-01, 7.50000000e-01, 4.36234494e-01, 7.00327585e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 5.20181427e-01}, + {0.00000000e+00, 4.77250219e-01, 5.36249708e+00, 2.52383415e-01, + 5.75921529e+00, 1.26868736e+00, 2.51452613e-01, 6.38679494e-01, + 8.30167261e-01, 2.33516284e-01, 7.93249084e-01, 2.20539708e-01, + 3.09639510e-01, 4.86768283e+00, 4.28138134e-01, 8.50001386e-01, + 6.08526572e-01, 9.48166786e-01, 7.43109762e-01, 2.67977796e-01, + 1.78830543e-01, 7.50000000e-01, 3.07187296e-01, 1.10900996e+00, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 2.25768580e-01}, + {0.00000000e+00, 7.31790935e-01, 2.52383415e-01, 2.07020176e+01, + 2.13899571e-01, 1.79625278e-01, 3.94756499e-01, 2.72055621e-01, + 2.21322329e-01, 5.81174860e-01, 2.41148280e-01, 4.92657548e-01, + 4.99414247e-01, 3.00383113e-01, 2.69170214e-01, 2.95117493e-01, + 2.32476995e-01, 5.76114817e-01, 6.01733396e-01, 6.34451252e-01, + 3.02232769e-01, 7.50000000e-01, 3.07930831e-01, 2.23671408e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 5.28325329e-01}, + {0.00000000e+00, 4.50905428e-01, 5.75921529e+00, 2.13899571e-01, + 9.10634223e+00, 1.63552603e+00, 2.34434106e-01, 5.40654444e-01, + 5.94384672e-01, 2.14047157e-01, 6.76875328e-01, 1.97166761e-01, + 2.51946380e-01, 1.58444827e+00, 4.52407652e-01, 7.63275386e-01, + 4.76639455e-01, 7.74041684e-01, 6.10882957e-01, 2.44693975e-01, + 1.44774364e-01, 7.50000000e-01, 2.45193294e-01, 1.30286928e+00, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 2.03968665e-01}, + {0.00000000e+00, 7.03141318e-01, 1.26868736e+00, 1.79625278e-01, + 1.63552603e+00, 6.99475019e+00, 2.48970812e-01, 3.99359878e-01, + 9.01259830e-01, 2.64258683e-01, 1.19492933e+00, 2.75527698e-01, + 4.28576433e-01, 8.11140936e-01, 5.81414067e-01, 1.90643780e+00, + 8.31863127e-01, 8.45039731e-01, 6.85529376e-01, 3.69379335e-01, + 2.40807712e-01, 7.50000000e-01, 3.32874109e-01, 5.05418243e+00, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 2.70986883e-01}, + {0.00000000e+00, 3.96805472e-01, 2.51452613e-01, 3.94756499e-01, + 2.34434106e-01, 2.48970812e-01, 9.48547379e+00, 2.48796540e-01, + 5.71854758e-01, 8.40573731e-01, 2.82718808e-01, 1.11429351e+00, + 8.93330223e-01, 2.72679266e-01, 2.37160811e-01, 2.85158291e-01, + 2.87269045e-01, 3.69100895e-01, 4.45199900e-01, 6.48888163e-01, + 1.08939386e+00, 7.50000000e-01, 2.78024963e+00, 2.62771902e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 1.00399896e+00}, + {0.00000000e+00, 9.57108118e-01, 6.38679494e-01, 2.72055621e-01, + 5.40654444e-01, 3.99359878e-01, 2.48796540e-01, 7.88244144e+00, + 3.87151686e-01, 1.84455102e-01, 4.83435068e-01, 2.10542976e-01, + 2.86067872e-01, 7.60943081e-01, 3.47300820e-01, 4.24585221e-01, + 3.77442786e-01, 7.84315177e-01, 4.92238430e-01, 2.50954947e-01, + 2.64396616e-01, 7.50000000e-01, 2.29516029e-01, 4.08980256e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 2.00030947e-01}, + {0.00000000e+00, 5.14428496e-01, 8.30167261e-01, 2.21322329e-01, + 5.94384672e-01, 9.01259830e-01, 5.71854758e-01, 3.87151686e-01, + 1.60694674e+01, 2.57933576e-01, 7.40110055e-01, 3.14299881e-01, + 4.32076355e-01, 1.12425153e+00, 4.19656882e-01, 1.31555625e+00, + 9.25536475e-01, 6.61407005e-01, 5.39570743e-01, 2.88933760e-01, + 3.90225420e-01, 7.50000000e-01, 1.81930455e+00, 1.05926315e+00, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 2.91587251e-01}, + {0.00000000e+00, 5.43302985e-01, 2.33516284e-01, 5.81174860e-01, + 2.14047157e-01, 2.64258683e-01, 8.40573731e-01, 1.84455102e-01, + 2.57933576e-01, 4.86762150e+00, 3.13345237e-01, 1.66499837e+00, + 1.51247384e+00, 2.57799519e-01, 2.85790430e-01, 3.09071252e-01, + 2.99348100e-01, 3.78995471e-01, 7.00511896e-01, 2.49584558e+00, + 3.43150987e-01, 7.50000000e-01, 5.39308441e-01, 2.81349188e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 2.95548558e+00}, + {0.00000000e+00, 7.22895459e-01, 7.93249084e-01, 2.41148280e-01, + 6.76875328e-01, 1.19492933e+00, 2.82718808e-01, 4.83435068e-01, + 7.40110055e-01, 3.13345237e-01, 6.32564527e+00, 3.56851811e-01, + 5.34403407e-01, 9.38398440e-01, 5.96694133e-01, 1.52403165e+00, + 2.19214139e+00, 8.20193974e-01, 7.35729790e-01, 3.70194033e-01, + 2.41427653e-01, 7.50000000e-01, 4.08501941e-01, 1.32044155e+00, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 3.39320970e-01}, + {0.00000000e+00, 5.04576848e-01, 2.20539708e-01, 4.92657548e-01, + 1.97166761e-01, 2.75527698e-01, 1.11429351e+00, 2.10542976e-01, + 3.14299881e-01, 1.66499837e+00, 3.56851811e-01, 4.46305621e+00, + 2.12274889e+00, 2.49692056e-01, 3.03099202e-01, 4.06904090e-01, + 3.62830591e-01, 3.68478686e-01, 5.60836408e-01, 1.22050154e+00, + 4.38789464e-01, 7.50000000e-01, 5.80503535e-01, 3.25631695e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 3.33558735e+00}, + {0.00000000e+00, 6.25509899e-01, 3.09639510e-01, 4.99414247e-01, + 2.51946380e-01, 4.28576433e-01, 8.93330223e-01, 2.86067872e-01, + 4.32076355e-01, 1.51247384e+00, 5.34403407e-01, 2.12274889e+00, + 8.88346290e+00, 3.81598352e-01, 3.61925345e-01, 8.86557630e-01, + 5.05866341e-01, 4.98438721e-01, 7.57959723e-01, 1.22414515e+00, + 5.60653516e-01, 7.50000000e-01, 5.49937808e-01, 6.03240148e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 1.87684042e+00}, + {0.00000000e+00, 5.10109254e-01, 4.86768283e+00, 3.00383113e-01, + 1.58444827e+00, 8.11140936e-01, 2.72679266e-01, 7.60943081e-01, + 1.12425153e+00, 2.57799519e-01, 9.38398440e-01, 2.49692056e-01, + 3.81598352e-01, 8.96275887e+00, 3.97867522e-01, 9.58172021e-01, + 7.73025258e-01, 1.16534759e+00, 9.08032132e-01, 2.97018980e-01, + 2.21307752e-01, 7.50000000e-01, 3.84510481e-01, 8.67215281e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 2.52958933e-01}, + {0.00000000e+00, 7.71095354e-01, 4.28138134e-01, 2.69170214e-01, + 4.52407652e-01, 5.81414067e-01, 2.37160811e-01, 3.47300820e-01, + 4.19656882e-01, 2.85790430e-01, 5.96694133e-01, 3.03099202e-01, + 3.61925345e-01, 3.97867522e-01, 1.51545798e+01, 5.37994539e-01, + 4.45589871e-01, 6.51704179e-01, 5.60357280e-01, 3.70233083e-01, + 1.78033069e-01, 7.50000000e-01, 2.57545983e-01, 5.64854837e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 2.96124685e-01}, + {0.00000000e+00, 6.95763501e-01, 8.50001386e-01, 2.95117493e-01, + 7.63275386e-01, 1.90643780e+00, 2.85158291e-01, 4.24585221e-01, + 1.31555625e+00, 3.09071252e-01, 1.52403165e+00, 4.06904090e-01, + 8.86557630e-01, 9.58172021e-01, 5.37994539e-01, 8.33990474e+00, + 1.39424540e+00, 8.58988713e-01, 7.24369298e-01, 4.10943414e-01, + 4.07901262e-01, 7.50000000e-01, 4.61857147e-01, 4.36001721e+00, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 3.67482647e-01}, + {0.00000000e+00, 5.55324683e-01, 6.08526572e-01, 2.32476995e-01, + 4.76639455e-01, 8.31863127e-01, 2.87269045e-01, 3.77442786e-01, + 9.25536475e-01, 2.99348100e-01, 2.19214139e+00, 3.62830591e-01, + 5.05866341e-01, 7.73025258e-01, 4.45589871e-01, 1.39424540e+00, + 8.24459589e+00, 6.94509540e-01, 5.98385216e-01, 3.53719047e-01, + 2.94245493e-01, 7.50000000e-01, 4.17775411e-01, 1.04634306e+00, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 3.37250515e-01}, + {0.00000000e+00, 1.53463504e+00, 9.48166786e-01, 5.76114817e-01, + 7.74041684e-01, 8.45039731e-01, 3.69100895e-01, 7.84315177e-01, + 6.61407005e-01, 3.78995471e-01, 8.20193974e-01, 3.68478686e-01, + 4.98438721e-01, 1.16534759e+00, 6.51704179e-01, 8.58988713e-01, + 6.94509540e-01, 5.10577131e+00, 1.66260189e+00, 4.93679246e-01, + 2.70773669e-01, 7.50000000e-01, 4.62005069e-01, 8.50359559e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 3.72716393e-01}, + {0.00000000e+00, 9.79634362e-01, 7.43109762e-01, 6.01733396e-01, + 6.10882957e-01, 6.85529376e-01, 4.45199900e-01, 4.92238430e-01, + 5.39570743e-01, 7.00511896e-01, 7.35729790e-01, 5.60836408e-01, + 7.57959723e-01, 9.08032132e-01, 5.60357280e-01, 7.24369298e-01, + 5.98385216e-01, 1.66260189e+00, 6.20547751e+00, 8.91492247e-01, + 2.85084781e-01, 7.50000000e-01, 4.74451641e-01, 7.00342047e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 6.17118219e-01}, + {0.00000000e+00, 8.65874830e-01, 2.67977796e-01, 6.34451252e-01, + 2.44693975e-01, 3.69379335e-01, 6.48888163e-01, 2.50954947e-01, + 2.88933760e-01, 2.49584558e+00, 3.70194033e-01, 1.22050154e+00, + 1.22414515e+00, 2.97018980e-01, 3.70233083e-01, 4.10943414e-01, + 3.53719047e-01, 4.93679246e-01, 8.91492247e-01, 4.58356287e+00, + 3.42175943e-01, 7.50000000e-01, 4.89223745e-01, 3.85230939e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 1.73439753e+00}, + {0.00000000e+00, 3.09281009e-01, 1.78830543e-01, 3.02232769e-01, + 1.44774364e-01, 2.40807712e-01, 1.08939386e+00, 2.64396616e-01, + 3.90225420e-01, 3.43150987e-01, 2.41427653e-01, 4.38789464e-01, + 5.60653516e-01, 2.21307752e-01, 1.78033069e-01, 4.07901262e-01, + 2.94245493e-01, 2.70773669e-01, 2.85084781e-01, 3.42175943e-01, + 4.15522183e+01, 7.50000000e-01, 2.03605072e+00, 3.04533429e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 4.00252232e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 4.36234494e-01, 3.07187296e-01, 3.07930831e-01, + 2.45193294e-01, 3.32874109e-01, 2.78024963e+00, 2.29516029e-01, + 1.81930455e+00, 5.39308441e-01, 4.08501941e-01, 5.80503535e-01, + 5.49937808e-01, 3.84510481e-01, 2.57545983e-01, 4.61857147e-01, + 4.17775411e-01, 4.62005069e-01, 4.74451641e-01, 4.89223745e-01, + 2.03605072e+00, 7.50000000e-01, 1.21940332e+01, 3.82065335e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 5.63904098e-01}, + {0.00000000e+00, 7.00327585e-01, 1.10900996e+00, 2.23671408e-01, + 1.30286928e+00, 5.05418243e+00, 2.62771902e-01, 4.08980256e-01, + 1.05926315e+00, 2.81349188e-01, 1.32044155e+00, 3.25631695e-01, + 6.03240148e-01, 8.67215281e-01, 5.64854837e-01, 4.36001721e+00, + 1.04634306e+00, 8.50359559e-01, 7.00342047e-01, 3.85230939e-01, + 3.04533429e-01, 7.50000000e-01, 3.82065335e-01, 4.78944345e+00, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 3.07788194e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 1.40000000e-01, 1.40000000e-01, 1.40000000e-01, + 1.40000000e-01, 1.40000000e-01, 1.40000000e-01, 1.40000000e-01, + 1.40000000e-01, 1.40000000e-01, 1.40000000e-01, 1.40000000e-01, + 1.40000000e-01, 1.40000000e-01, 1.40000000e-01, 1.40000000e-01, + 1.40000000e-01, 1.40000000e-01, 1.40000000e-01, 1.40000000e-01, + 1.40000000e-01, 1.40000000e-01, 1.40000000e-01, 1.40000000e-01, + 1.40000000e-01, 1.33300000e+00, 1.40000000e-01, 1.40000000e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 5.20181427e-01, 2.25768580e-01, 5.28325329e-01, + 2.03968665e-01, 2.70986883e-01, 1.00399896e+00, 2.00030947e-01, + 2.91587251e-01, 2.95548558e+00, 3.39320970e-01, 3.33558735e+00, + 1.87684042e+00, 2.52958933e-01, 2.96124685e-01, 3.67482647e-01, + 3.37250515e-01, 3.72716393e-01, 6.17118219e-01, 1.73439753e+00, + 4.00252232e-01, 7.50000000e-01, 5.63904098e-01, 3.07788194e-01, + 7.50000000e-01, 1.40000000e-01, 7.50000000e-01, 3.18242650e+00}}; + + +/** Underlying frequency ratios for BLOSUM90 */ +static const double BLOSUM90_FREQRATIOS[BLASTAA_SIZE][BLASTAA_SIZE] = +{{0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00}, + {0.00000000e+00, 5.49812903e+00, 4.38682591e-01, 6.69393543e-01, + 4.10593787e-01, 6.67362415e-01, 3.46907686e-01, 8.55253386e-01, + 4.61993747e-01, 4.75838329e-01, 6.64089300e-01, 4.46304927e-01, + 5.48948177e-01, 4.75350260e-01, 7.12075051e-01, 6.37043670e-01, + 5.31189855e-01, 1.45279278e+00, 9.34706358e-01, 7.93712275e-01, + 2.83637106e-01, 7.50000000e-01, 3.70274337e-01, 6.55892238e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 4.58271706e-01}, + {0.00000000e+00, 4.38682591e-01, 5.85593083e+00, 2.24322103e-01, + 6.24911512e+00, 1.23816514e+00, 2.14608760e-01, 5.64122407e-01, + 7.82997018e-01, 1.96621553e-01, 7.38810616e-01, 1.94127812e-01, + 2.69937418e-01, 5.34266045e+00, 3.78549747e-01, 7.81147613e-01, + 5.45762328e-01, 8.70271567e-01, 6.66528685e-01, 2.28904046e-01, + 1.42810925e-01, 7.50000000e-01, 2.77062678e-01, 1.06526643e+00, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 1.95138263e-01}, + {0.00000000e+00, 6.69393543e-01, 2.24322103e-01, 2.16370056e+01, + 1.76054992e-01, 1.47039970e-01, 3.92579579e-01, 2.18182122e-01, + 1.93552170e-01, 5.20896357e-01, 2.15679622e-01, 4.25126435e-01, + 4.27104871e-01, 2.87330927e-01, 2.30151009e-01, 2.42068607e-01, + 2.06242089e-01, 5.23756471e-01, 5.48342086e-01, 5.88014943e-01, + 2.50594014e-01, 7.50000000e-01, 2.92512133e-01, 1.82991170e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 4.63931905e-01}, + {0.00000000e+00, 4.10593787e-01, 6.24911512e+00, 1.76054992e-01, + 9.87094836e+00, 1.61092322e+00, 1.97472144e-01, 4.84699282e-01, + 5.46208942e-01, 1.71646922e-01, 6.20942905e-01, 1.71021465e-01, + 2.15937410e-01, 1.52110375e+00, 3.99848977e-01, 6.86343892e-01, + 4.17577647e-01, 6.96691332e-01, 5.29001708e-01, 2.06199101e-01, + 1.15609648e-01, 7.50000000e-01, 2.13739536e-01, 1.26113669e+00, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 1.71274897e-01}, + {0.00000000e+00, 6.67362415e-01, 1.23816514e+00, 1.47039970e-01, + 1.61092322e+00, 7.91072600e+00, 2.04920324e-01, 3.47008914e-01, + 7.72534981e-01, 2.37473636e-01, 1.07737256e+00, 2.37238651e-01, + 3.61519510e-01, 7.51559518e-01, 5.28989676e-01, 1.83144568e+00, + 7.31811567e-01, 7.59912485e-01, 6.13331345e-01, 3.33942314e-01, + 2.02669731e-01, 7.50000000e-01, 2.73296982e-01, 5.61081481e+00, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 2.37333866e-01}, + {0.00000000e+00, 3.46907686e-01, 2.14608760e-01, 3.92579579e-01, + 1.97472144e-01, 2.04920324e-01, 1.05190688e+01, 1.97046381e-01, + 5.10867985e-01, 7.73236776e-01, 2.73932585e-01, 1.05058955e+00, + 8.11125795e-01, 2.36979230e-01, 2.12448013e-01, 2.80742738e-01, + 2.62210098e-01, 3.33306129e-01, 3.90547453e-01, 5.77272331e-01, + 9.82154073e-01, 7.50000000e-01, 2.77172979e+00, 2.33605433e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 9.38207661e-01}, + {0.00000000e+00, 8.55253386e-01, 5.64122407e-01, 2.18182122e-01, + 4.84699282e-01, 3.47008914e-01, 1.97046381e-01, 8.29576319e+00, + 3.35169505e-01, 1.51428808e-01, 4.27685370e-01, 1.83265898e-01, + 2.37360208e-01, 6.67802895e-01, 2.98989408e-01, 3.80871171e-01, + 3.28608745e-01, 7.00460225e-01, 4.00474342e-01, 2.02269779e-01, + 2.34021877e-01, 7.50000000e-01, 1.88904853e-01, 3.59819671e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 1.70365676e-01}, + {0.00000000e+00, 4.61993747e-01, 7.82997018e-01, 1.93552170e-01, + 5.46208942e-01, 7.72534981e-01, 5.10867985e-01, 3.35169505e-01, + 1.85636930e+01, 2.32236241e-01, 6.80230209e-01, 2.89296785e-01, + 4.06641319e-01, 1.09210477e+00, 3.99528365e-01, 1.24778653e+00, + 8.70382542e-01, 5.94270003e-01, 4.71855284e-01, 2.49881715e-01, + 3.84396440e-01, 7.50000000e-01, 1.62620965e+00, 9.52331980e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 2.66176152e-01}, + {0.00000000e+00, 4.75838329e-01, 1.96621553e-01, 5.20896357e-01, + 1.71646922e-01, 2.37473636e-01, 7.73236776e-01, 1.51428808e-01, + 2.32236241e-01, 5.62471003e+00, 2.84042165e-01, 1.59032509e+00, + 1.47982983e+00, 2.29223921e-01, 2.60766373e-01, 2.80218761e-01, + 2.69951432e-01, 3.32350183e-01, 6.20404233e-01, 2.47466358e+00, + 2.84494454e-01, 7.50000000e-01, 4.78737209e-01, 2.53644956e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 3.22503669e+00}, + {0.00000000e+00, 6.64089300e-01, 7.38810616e-01, 2.15679622e-01, + 6.20942905e-01, 1.07737256e+00, 2.73932585e-01, 4.27685370e-01, + 6.80230209e-01, 2.84042165e-01, 7.40971353e+00, 3.18645229e-01, + 5.03980900e-01, 8.92677413e-01, 5.54085013e-01, 1.48494120e+00, + 2.15988586e+00, 7.31921781e-01, 6.80549543e-01, 3.28415139e-01, + 1.74946435e-01, 7.50000000e-01, 3.46172886e-01, 1.23156378e+00, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 3.04624249e-01}, + {0.00000000e+00, 4.46304927e-01, 1.94127812e-01, 4.25126435e-01, + 1.71021465e-01, 2.37238651e-01, 1.05058955e+00, 1.83265898e-01, + 2.89296785e-01, 1.59032509e+00, 3.18645229e-01, 5.03145573e+00, + 2.07702392e+00, 2.24291285e-01, 2.72188366e-01, 3.74709469e-01, + 3.26170372e-01, 3.38914311e-01, 5.07385737e-01, 1.13293282e+00, + 3.72830750e-01, 7.50000000e-01, 5.08794972e-01, 2.89246563e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 3.63712766e+00}, + {0.00000000e+00, 5.48948177e-01, 2.69937418e-01, 4.27104871e-01, + 2.15937410e-01, 3.61519510e-01, 8.11125795e-01, 2.37360208e-01, + 4.06641319e-01, 1.47982983e+00, 5.03980900e-01, 2.07702392e+00, + 1.13394084e+01, 3.40430076e-01, 3.16108504e-01, 8.49621832e-01, + 4.63059321e-01, 4.39100157e-01, 6.79030953e-01, 1.13727027e+00, + 4.92730381e-01, 7.50000000e-01, 4.80316866e-01, 5.46178209e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 1.83504401e+00}, + {0.00000000e+00, 4.75350260e-01, 5.34266045e+00, 2.87330927e-01, + 1.52110375e+00, 7.51559518e-01, 2.36979230e-01, 6.67802895e-01, + 1.09210477e+00, 2.29223921e-01, 8.92677413e-01, 2.24291285e-01, + 3.40430076e-01, 1.03313947e+01, 3.50745318e-01, 9.04906229e-01, + 7.13097098e-01, 1.09686657e+00, 8.46059068e-01, 2.58543521e-01, + 1.78320001e-01, 7.50000000e-01, 3.59725936e-01, 8.09573592e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 2.26289963e-01}, + {0.00000000e+00, 7.12075051e-01, 3.78549747e-01, 2.30151009e-01, + 3.99848977e-01, 5.28989676e-01, 2.12448013e-01, 2.98989408e-01, + 3.99528365e-01, 2.60766373e-01, 5.54085013e-01, 2.72188366e-01, + 3.16108504e-01, 3.50745318e-01, 1.60877707e+01, 4.86643270e-01, + 4.09306303e-01, 5.93677872e-01, 4.80576271e-01, 3.23032809e-01, + 1.74477792e-01, 7.50000000e-01, 2.13074361e-01, 5.12969199e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 2.67560235e-01}, + {0.00000000e+00, 6.37043670e-01, 7.81147613e-01, 2.42068607e-01, + 6.86343892e-01, 1.83144568e+00, 2.80742738e-01, 3.80871171e-01, + 1.24778653e+00, 2.80218761e-01, 1.48494120e+00, 3.74709469e-01, + 8.49621832e-01, 9.04906229e-01, 4.86643270e-01, 9.98642121e+00, + 1.25897253e+00, 7.86784913e-01, 6.71486014e-01, 3.48766905e-01, + 3.51106744e-01, 7.50000000e-01, 4.01714365e-01, 4.91663315e+00, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 3.36422330e-01}, + {0.00000000e+00, 5.31189855e-01, 5.45762328e-01, 2.06242089e-01, + 4.17577647e-01, 7.31811567e-01, 2.62210098e-01, 3.28608745e-01, + 8.70382542e-01, 2.69951432e-01, 2.15988586e+00, 3.26170372e-01, + 4.63059321e-01, 7.13097098e-01, 4.09306303e-01, 1.25897253e+00, + 9.45518393e+00, 6.27094667e-01, 5.25362150e-01, 3.16415957e-01, + 2.48889195e-01, 7.50000000e-01, 3.62347797e-01, 9.31246918e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 3.03390753e-01}, + {0.00000000e+00, 1.45279278e+00, 8.70271567e-01, 5.23756471e-01, + 6.96691332e-01, 7.59912485e-01, 3.33306129e-01, 7.00460225e-01, + 5.94270003e-01, 3.32350183e-01, 7.31921781e-01, 3.38914311e-01, + 4.39100157e-01, 1.09686657e+00, 5.93677872e-01, 7.86784913e-01, + 6.27094667e-01, 5.99164849e+00, 1.62868240e+00, 4.49399804e-01, + 2.30316670e-01, 7.50000000e-01, 4.11583504e-01, 7.70078852e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 3.36254561e-01}, + {0.00000000e+00, 9.34706358e-01, 6.66528685e-01, 5.48342086e-01, + 5.29001708e-01, 6.13331345e-01, 3.90547453e-01, 4.00474342e-01, + 4.71855284e-01, 6.20404233e-01, 6.80549543e-01, 5.07385737e-01, + 6.79030953e-01, 8.46059068e-01, 4.80576271e-01, 6.71486014e-01, + 5.25362150e-01, 1.62868240e+00, 7.25868130e+00, 8.04058132e-01, + 2.27083637e-01, 7.50000000e-01, 4.20544390e-01, 6.35332398e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 5.53180238e-01}, + {0.00000000e+00, 7.93712275e-01, 2.28904046e-01, 5.88014943e-01, + 2.06199101e-01, 3.33942314e-01, 5.77272331e-01, 2.02269779e-01, + 2.49881715e-01, 2.47466358e+00, 3.28415139e-01, 1.13293282e+00, + 1.13727027e+00, 2.58543521e-01, 3.23032809e-01, 3.48766905e-01, + 3.16415957e-01, 4.49399804e-01, 8.04058132e-01, 5.31607752e+00, + 2.99756311e-01, 7.50000000e-01, 4.04621372e-01, 3.39550748e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 1.67659508e+00}, + {0.00000000e+00, 2.83637106e-01, 1.42810925e-01, 2.50594014e-01, + 1.15609648e-01, 2.02669731e-01, 9.82154073e-01, 2.34021877e-01, + 3.84396440e-01, 2.84494454e-01, 1.74946435e-01, 3.72830750e-01, + 4.92730381e-01, 1.78320001e-01, 1.74477792e-01, 3.51106744e-01, + 2.48889195e-01, 2.30316670e-01, 2.27083637e-01, 2.99756311e-01, + 4.26750567e+01, 7.50000000e-01, 1.77446682e+00, 2.58826370e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 3.37037347e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 3.70274337e-01, 2.77062678e-01, 2.92512133e-01, + 2.13739536e-01, 2.73296982e-01, 2.77172979e+00, 1.88904853e-01, + 1.62620965e+00, 4.78737209e-01, 3.46172886e-01, 5.08794972e-01, + 4.80316866e-01, 3.59725936e-01, 2.13074361e-01, 4.01714365e-01, + 3.62347797e-01, 4.11583504e-01, 4.20544390e-01, 4.04621372e-01, + 1.77446682e+00, 7.50000000e-01, 1.36090374e+01, 3.21879801e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 4.96615724e-01}, + {0.00000000e+00, 6.55892238e-01, 1.06526643e+00, 1.82991170e-01, + 1.26113669e+00, 5.61081481e+00, 2.33605433e-01, 3.59819671e-01, + 9.52331980e-01, 2.53644956e-01, 1.23156378e+00, 2.89246563e-01, + 5.46178209e-01, 8.09573592e-01, 5.12969199e-01, 4.91663315e+00, + 9.31246918e-01, 7.70078852e-01, 6.35332398e-01, 3.39550748e-01, + 2.58826370e-01, 7.50000000e-01, 3.21879801e-01, 5.34819225e+00, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 2.74820979e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 1.20000000e-01, 1.20000000e-01, 1.20000000e-01, + 1.20000000e-01, 1.20000000e-01, 1.20000000e-01, 1.20000000e-01, + 1.20000000e-01, 1.20000000e-01, 1.20000000e-01, 1.20000000e-01, + 1.20000000e-01, 1.20000000e-01, 1.20000000e-01, 1.20000000e-01, + 1.20000000e-01, 1.20000000e-01, 1.20000000e-01, 1.20000000e-01, + 1.20000000e-01, 1.20000000e-01, 1.20000000e-01, 1.20000000e-01, + 1.20000000e-01, 1.33300000e+00, 1.20000000e-01, 1.20000000e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 4.58271706e-01, 1.95138263e-01, 4.63931905e-01, + 1.71274897e-01, 2.37333866e-01, 9.38207661e-01, 1.70365676e-01, + 2.66176152e-01, 3.22503669e+00, 3.04624249e-01, 3.63712766e+00, + 1.83504401e+00, 2.26289963e-01, 2.67560235e-01, 3.36422330e-01, + 3.03390753e-01, 3.36254561e-01, 5.53180238e-01, 1.67659508e+00, + 3.37037347e-01, 7.50000000e-01, 4.96615724e-01, 2.74820979e-01, + 7.50000000e-01, 1.20000000e-01, 7.50000000e-01, 3.47015056e+00}}; + + +/** Underlying frequency ratios for PAM30 */ +static const double PAM30_FREQRATIOS[BLASTAA_SIZE][BLASTAA_SIZE] = +{{0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00}, + {0.00000000e+00, 7.78912912e+00, 3.02026381e-01, 1.07971742e-01, + 3.16807704e-01, 4.53014491e-01, 5.67768414e-02, 5.75744366e-01, + 8.33329143e-02, 1.98631007e-01, 9.54324770e-02, 1.15281887e-01, + 1.89338526e-01, 2.84890839e-01, 5.93402017e-01, 2.35344432e-01, + 9.10275603e-02, 8.75340369e-01, 8.27084092e-01, 4.77504036e-01, + 9.76867708e-03, 7.50000000e-01, 6.99222384e-02, 3.58158006e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 1.40431646e-01}, + {0.00000000e+00, 3.02026381e-01, 8.11766307e+00, 1.52757717e-02, + 8.45582330e+00, 1.47233532e+00, 2.69459344e-02, 3.37867839e-01, + 6.63755159e-01, 1.25827363e-01, 4.40914175e-01, 4.89221062e-02, + 3.39137767e-02, 7.72564409e+00, 1.02509984e-01, 3.67813095e-01, + 8.23817770e-02, 6.03773710e-01, 3.53305100e-01, 6.71234492e-02, + 3.23783774e-02, 7.50000000e-01, 1.13742904e-01, 9.91005473e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 7.21274943e-02}, + {0.00000000e+00, 1.07971742e-01, 1.52757717e-02, 2.75882699e+01, + 7.71413760e-03, 7.86819951e-03, 1.16056875e-02, 4.15734061e-02, + 7.76695875e-02, 1.19064797e-01, 7.78302876e-03, 6.02160155e-03, + 9.46008155e-03, 2.40417467e-02, 6.32617140e-02, 8.13944094e-03, + 6.80179741e-02, 3.79110970e-01, 6.77518036e-02, 1.32369559e-01, + 4.67559620e-03, 7.50000000e-01, 2.57579768e-01, 7.98640138e-03, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 4.01312493e-02}, + {0.00000000e+00, 3.16807704e-01, 8.45582330e+00, 7.71413760e-03, + 1.42349183e+01, 2.34246282e+00, 6.53544516e-03, 3.26331184e-01, + 2.71287596e-01, 7.98669838e-02, 2.19798242e-01, 1.38483017e-02, + 2.29035955e-02, 1.75629226e+00, 6.80928461e-02, 4.27289142e-01, + 2.95026156e-02, 2.86012439e-01, 1.99709663e-01, 6.47860871e-02, + 5.42804139e-03, 7.50000000e-01, 1.97046510e-02, 1.50786644e+00, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 3.37687752e-02}, + {0.00000000e+00, 4.53014491e-01, 1.47233532e+00, 7.86819951e-03, + 2.34246282e+00, 1.36624533e+01, 7.86638136e-03, 2.31589040e-01, + 1.81505594e-01, 1.49736909e-01, 2.26119978e-01, 4.27891596e-02, + 9.24578171e-02, 4.63622671e-01, 1.53529752e-01, 1.50713628e+00, + 4.28627549e-02, 2.25474962e-01, 1.30340902e-01, 1.09854687e-01, + 3.23568839e-03, 7.50000000e-01, 5.65446816e-02, 8.36539662e+00, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 7.50595678e-02}, + {0.00000000e+00, 5.67768414e-02, 2.69459344e-02, 1.16056875e-02, + 6.53544516e-03, 7.86638136e-03, 2.14068164e+01, 4.44753676e-02, + 1.31584637e-01, 4.73011553e-01, 8.55473177e-03, 4.15676862e-01, + 2.49857403e-01, 5.06072009e-02, 3.56716332e-02, 1.12390799e-02, + 4.18628794e-02, 1.13897780e-01, 5.14073961e-02, 6.66400959e-02, + 2.13219283e-01, 7.50000000e-01, 1.78617980e+00, 9.33613946e-03, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 4.32977029e-01}, + {0.00000000e+00, 5.75744366e-01, 3.37867839e-01, 4.15734061e-02, + 3.26331184e-01, 2.31589040e-01, 4.44753676e-02, 9.31368857e+00, + 4.78871557e-02, 2.41395424e-02, 8.59403011e-02, 2.78096704e-02, + 5.67940501e-02, 3.51241937e-01, 1.21951553e-01, 9.44408638e-02, + 3.97493058e-02, 5.52895766e-01, 1.35075690e-01, 1.48994667e-01, + 5.68401675e-03, 7.50000000e-01, 9.25861867e-03, 1.71822465e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 2.67022462e-02}, + {0.00000000e+00, 8.33329143e-02, 6.63755159e-01, 7.76695875e-02, + 2.71287596e-01, 1.81505594e-01, 1.31584637e-01, 4.78871557e-02, + 2.29276353e+01, 4.36863126e-02, 1.19631443e-01, 1.23251186e-01, + 2.78547216e-02, 1.11873100e+00, 2.47982049e-01, 1.35964891e+00, + 5.86952720e-01, 1.31501535e-01, 8.61653901e-02, 1.14978024e-01, + 8.12035191e-02, 7.50000000e-01, 3.23746871e-01, 6.94918113e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 9.92432853e-02}, + {0.00000000e+00, 1.98631007e-01, 1.25827363e-01, 1.19064797e-01, + 7.98669838e-02, 1.49736909e-01, 4.73011553e-01, 2.41395424e-02, + 4.36863126e-02, 1.86346064e+01, 1.27210180e-01, 6.43409762e-01, + 7.92524183e-01, 1.79107849e-01, 5.32543187e-02, 7.03383168e-02, + 1.58533228e-01, 9.68805230e-02, 4.44355269e-01, 1.93788392e+00, + 9.23811792e-03, 7.50000000e-01, 1.18573178e-01, 1.15136508e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 6.07207247e+00}, + {0.00000000e+00, 9.54324770e-02, 4.40914175e-01, 7.78302876e-03, + 2.19798242e-01, 2.26119978e-01, 8.55473177e-03, 8.59403011e-02, + 1.19631443e-01, 1.27210180e-01, 9.98780825e+00, 6.07412260e-02, + 5.62560724e-01, 6.97247226e-01, 1.06485246e-01, 3.84266618e-01, + 1.11121735e+00, 2.59857530e-01, 3.36316040e-01, 4.62631009e-02, + 1.75142268e-02, 7.50000000e-01, 4.16645387e-02, 2.95037286e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 8.07975645e-02}, + {0.00000000e+00, 1.15281887e-01, 4.89221062e-02, 6.02160155e-03, + 1.38483017e-02, 4.27891596e-02, 4.15676862e-01, 2.78096704e-02, + 1.23251186e-01, 6.43409762e-01, 6.07412260e-02, 1.00194105e+01, + 1.24218990e+00, 8.95821132e-02, 9.04970355e-02, 1.84988987e-01, + 5.31753716e-02, 5.83025512e-02, 1.02884887e-01, 4.62744892e-01, + 1.27405990e-01, 7.50000000e-01, 9.50388211e-02, 1.04757148e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 7.19029657e+00}, + {0.00000000e+00, 1.89338526e-01, 3.39137767e-02, 9.46008155e-03, + 2.29035955e-02, 9.24578171e-02, 2.49857403e-01, 5.67940501e-02, + 2.78547216e-02, 7.92524183e-01, 5.62560724e-01, 1.24218990e+00, + 4.65953079e+01, 4.66775487e-02, 6.48026667e-02, 2.71732304e-01, + 2.43002286e-01, 1.60899996e-01, 2.62669188e-01, 6.30878126e-01, + 1.29243603e-02, 7.50000000e-01, 2.12719960e-02, 1.70582240e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 1.10650779e+00}, + {0.00000000e+00, 2.84890839e-01, 7.72564409e+00, 2.40417467e-02, + 1.75629226e+00, 4.63622671e-01, 5.06072009e-02, 3.51241937e-01, + 1.11873100e+00, 1.79107849e-01, 6.97247226e-01, 8.95821132e-02, + 4.66775487e-02, 1.46457342e+01, 1.42408737e-01, 2.98864303e-01, + 1.43682999e-01, 9.72144796e-01, 5.31363673e-01, 6.98330827e-02, + 6.36210916e-02, 7.50000000e-01, 2.22758622e-01, 3.91824098e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 1.16595604e-01}, + {0.00000000e+00, 5.93402017e-01, 1.02509984e-01, 6.32617140e-02, + 6.80928461e-02, 1.53529752e-01, 3.56716332e-02, 1.21951553e-01, + 2.47982049e-01, 5.32543187e-02, 1.06485246e-01, 9.04970355e-02, + 6.48026667e-02, 1.42408737e-01, 1.58088136e+01, 3.76066261e-01, + 2.69813309e-01, 5.70573606e-01, 2.40194503e-01, 1.40453450e-01, + 9.02230244e-03, 7.50000000e-01, 9.80486654e-03, 2.50506944e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 7.92594202e-02}, + {0.00000000e+00, 2.35344432e-01, 3.67813095e-01, 8.13944094e-03, + 4.27289142e-01, 1.50713628e+00, 1.12390799e-02, 9.44408638e-02, + 1.35964891e+00, 7.03383168e-02, 3.84266618e-01, 1.84988987e-01, + 2.71732304e-01, 2.98864303e-01, 3.76066261e-01, 1.81380436e+01, + 5.84920778e-01, 1.67743101e-01, 1.50331260e-01, 1.02742037e-01, + 1.20726953e-02, 7.50000000e-01, 1.69239569e-02, 8.75457039e+00, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 1.50394300e-01}, + {0.00000000e+00, 9.10275603e-02, 8.23817770e-02, 6.80179741e-02, + 2.95026156e-02, 4.28627549e-02, 4.18628794e-02, 3.97493058e-02, + 5.86952720e-01, 1.58533228e-01, 1.11121735e+00, 5.31753716e-02, + 2.43002286e-01, 1.43682999e-01, 2.69813309e-01, 5.84920778e-01, + 1.89240175e+01, 3.54610075e-01, 1.06409795e-01, 7.42726505e-02, + 5.17051285e-01, 7.50000000e-01, 3.01584225e-02, 2.79081365e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 8.49660454e-02}, + {0.00000000e+00, 8.75340369e-01, 6.03773710e-01, 3.79110970e-01, + 2.86012439e-01, 2.25474962e-01, 1.13897780e-01, 5.52895766e-01, + 1.31501535e-01, 9.68805230e-02, 2.59857530e-01, 5.83025512e-02, + 1.60899996e-01, 9.72144796e-01, 5.70573606e-01, 1.67743101e-01, + 3.54610075e-01, 9.02800848e+00, 1.14463515e+00, 1.17807053e-01, + 1.79210510e-01, 7.50000000e-01, 9.65415148e-02, 2.00316511e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 6.99430663e-02}, + {0.00000000e+00, 8.27084092e-01, 3.53305100e-01, 6.77518036e-02, + 1.99709663e-01, 1.30340902e-01, 5.14073961e-02, 1.35075690e-01, + 8.61653901e-02, 4.44355269e-01, 3.36316040e-01, 1.02884887e-01, + 2.62669188e-01, 5.31363673e-01, 2.40194503e-01, 1.50331260e-01, + 1.06409795e-01, 1.14463515e+00, 1.16950751e+01, 3.90871695e-01, + 1.25703039e-02, 7.50000000e-01, 1.11995399e-01, 1.39052321e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 2.05920141e-01}, + {0.00000000e+00, 4.77504036e-01, 6.71234492e-02, 1.32369559e-01, + 6.47860871e-02, 1.09854687e-01, 6.66400959e-02, 1.48994667e-01, + 1.14978024e-01, 1.93788392e+00, 4.62631009e-02, 4.62744892e-01, + 6.30878126e-01, 6.98330827e-02, 1.40453450e-01, 1.02742037e-01, + 7.42726505e-02, 1.17807053e-01, 3.90871695e-01, 1.16090589e+01, + 5.05672115e-03, 7.50000000e-01, 8.04763342e-02, 1.06755129e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 9.07853260e-01}, + {0.00000000e+00, 9.76867708e-03, 3.23783774e-02, 4.67559620e-03, + 5.42804139e-03, 3.23568839e-03, 2.13219283e-01, 5.68401675e-03, + 8.12035191e-02, 9.23811792e-03, 1.75142268e-02, 1.27405990e-01, + 1.29243603e-02, 6.36210916e-02, 9.02230244e-03, 1.20726953e-02, + 5.17051285e-01, 1.79210510e-01, 1.25703039e-02, 5.05672115e-03, + 8.86903633e+01, 7.50000000e-01, 1.73378233e-01, 7.08668846e-03, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 9.17500230e-02}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 6.99222384e-02, 1.13742904e-01, 2.57579768e-01, + 1.97046510e-02, 5.65446816e-02, 1.78617980e+00, 9.25861867e-03, + 3.23746871e-01, 1.18573178e-01, 4.16645387e-02, 9.50388211e-02, + 2.12719960e-02, 2.22758622e-01, 9.80486654e-03, 1.69239569e-02, + 3.01584225e-02, 9.65415148e-02, 1.11995399e-01, 8.04763342e-02, + 1.73378233e-01, 7.50000000e-01, 2.84454162e+01, 3.92787209e-02, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 1.02140077e-01}, + {0.00000000e+00, 3.58158006e-01, 9.91005473e-01, 7.98640138e-03, + 1.50786644e+00, 8.36539662e+00, 9.33613946e-03, 1.71822465e-01, + 6.94918113e-01, 1.15136508e-01, 2.95037286e-01, 1.04757148e-01, + 1.70582240e-01, 3.91824098e-01, 2.50506944e-01, 8.75457039e+00, + 2.79081365e-01, 2.00316511e-01, 1.39052321e-01, 1.06755129e-01, + 7.08668846e-03, 7.50000000e-01, 3.92787209e-02, 8.53499117e+00, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 1.07889016e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 3.00000000e-03, 3.00000000e-03, 3.00000000e-03, + 3.00000000e-03, 3.00000000e-03, 3.00000000e-03, 3.00000000e-03, + 3.00000000e-03, 3.00000000e-03, 3.00000000e-03, 3.00000000e-03, + 3.00000000e-03, 3.00000000e-03, 3.00000000e-03, 3.00000000e-03, + 3.00000000e-03, 3.00000000e-03, 3.00000000e-03, 3.00000000e-03, + 3.00000000e-03, 3.00000000e-03, 3.00000000e-03, 3.00000000e-03, + 3.00000000e-03, 1.33300000e+00, 3.00000000e-03, 3.00000000e-03}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 1.40431646e-01, 7.21274943e-02, 4.01312493e-02, + 3.37687752e-02, 7.50595678e-02, 4.32977029e-01, 2.67022462e-02, + 9.92432853e-02, 6.07207247e+00, 8.07975645e-02, 7.19029657e+00, + 1.10650779e+00, 1.16595604e-01, 7.92594202e-02, 1.50394300e-01, + 8.49660454e-02, 6.99430663e-02, 2.05920141e-01, 9.07853260e-01, + 9.17500230e-02, 7.50000000e-01, 1.02140077e-01, 1.07889016e-01, + 7.50000000e-01, 3.00000000e-03, 7.50000000e-01, 6.85288369e+00}}; + + +/** Underlying frequency ratios for PAM70 */ +static const double PAM70_FREQRATIOS[BLASTAA_SIZE][BLASTAA_SIZE] = +{{0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00}, + {0.00000000e+00, 4.89972946e+00, 6.04638890e-01, 2.42067963e-01, + 6.18433760e-01, 7.71213936e-01, 1.34833512e-01, 1.01498720e+00, + 2.21390981e-01, 4.34566961e-01, 2.49319770e-01, 2.49736666e-01, + 3.75829845e-01, 5.88646912e-01, 1.03045017e+00, 4.65602795e-01, + 2.26934456e-01, 1.34964157e+00, 1.32707297e+00, 8.08640731e-01, + 4.32618091e-02, 7.50000000e-01, 1.54222102e-01, 6.38034395e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 3.05507349e-01}, + {0.00000000e+00, 6.04638890e-01, 5.42142610e+00, 6.60540784e-02, + 5.88929014e+00, 2.24885858e+00, 7.88958522e-02, 6.48829213e-01, + 1.09845328e+00, 2.52789787e-01, 7.77906911e-01, 1.19715714e-01, + 1.39730820e-01, 4.87904539e+00, 2.73736473e-01, 7.90138593e-01, + 2.53425041e-01, 9.54703234e-01, 6.53688648e-01, 1.85063408e-01, + 7.80441148e-02, 7.50000000e-01, 2.23329473e-01, 1.61317606e+00, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 1.59869478e-01}, + {0.00000000e+00, 2.42067963e-01, 6.60540784e-02, 2.48333286e+01, + 3.97130183e-02, 3.87120726e-02, 5.48382532e-02, 1.15679023e-01, + 1.64834856e-01, 2.46442788e-01, 3.83065039e-02, 3.01944614e-02, + 4.42379868e-02, 9.65904773e-02, 1.58823055e-01, 3.93861754e-02, + 1.51302811e-01, 6.65193229e-01, 1.86417892e-01, 2.74442219e-01, + 2.22251459e-02, 7.50000000e-01, 5.23320532e-01, 3.90058338e-02, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 9.54452197e-02}, + {0.00000000e+00, 6.18433760e-01, 5.88929014e+00, 3.97130183e-02, + 8.89965209e+00, 3.35659301e+00, 3.36340414e-02, 6.39387415e-01, + 5.99746161e-01, 1.93031482e-01, 4.83846284e-01, 6.35580693e-02, + 1.03502951e-01, 2.39946804e+00, 2.12686642e-01, 9.22524100e-01, + 1.38560979e-01, 5.99713481e-01, 4.42110174e-01, 1.72922381e-01, + 2.64171458e-02, 7.50000000e-01, 7.98817496e-02, 2.29587193e+00, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 1.02625370e-01}, + {0.00000000e+00, 7.71213936e-01, 2.24885858e+00, 3.87120726e-02, + 3.35659301e+00, 8.65385421e+00, 3.82398528e-02, 4.91841782e-01, + 4.80150693e-01, 2.83623924e-01, 4.66181910e-01, 1.17487568e-01, + 2.12426533e-01, 9.64695360e-01, 3.47480663e-01, 2.25815498e+00, + 1.78914910e-01, 4.78627630e-01, 3.38114944e-01, 2.46972907e-01, + 1.89756038e-02, 7.50000000e-01, 1.19141830e-01, 5.86672974e+00, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 1.67617543e-01}, + {0.00000000e+00, 1.34833512e-01, 7.88958522e-02, 5.48382532e-02, + 3.36340414e-02, 3.82398528e-02, 1.74546228e+01, 1.00136016e-01, + 2.76577280e-01, 8.45971180e-01, 4.12894272e-02, 8.23804865e-01, + 5.08442478e-01, 1.31366508e-01, 8.91727264e-02, 5.25046736e-02, + 9.93639027e-02, 2.15603624e-01, 1.37600314e-01, 2.07856659e-01, + 4.52242030e-01, 7.50000000e-01, 3.37849562e+00, 4.44561913e-02, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 8.30493328e-01}, + {0.00000000e+00, 1.01498720e+00, 6.48829213e-01, 1.15679023e-01, + 6.39387415e-01, 4.91841782e-01, 1.00136016e-01, 7.30864335e+00, + 1.45138751e-01, 1.07004308e-01, 2.09861286e-01, 8.09786598e-02, + 1.44276056e-01, 6.59774806e-01, 3.08399509e-01, 2.37177521e-01, + 1.22086776e-01, 9.64187863e-01, 3.70424893e-01, 3.12986176e-01, + 2.68996967e-02, 7.50000000e-01, 4.37757319e-02, 3.80863925e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 8.88316375e-02}, + {0.00000000e+00, 2.21390981e-01, 1.09845328e+00, 1.64834856e-01, + 5.99746161e-01, 4.80150693e-01, 2.76577280e-01, 1.45138751e-01, + 1.64223829e+01, 1.39752446e-01, 3.35085062e-01, 2.59072609e-01, + 1.22395235e-01, 1.67658945e+00, 4.92867985e-01, 2.18832010e+00, + 1.05779530e+00, 3.26721114e-01, 2.30266854e-01, 2.33270842e-01, + 1.86063506e-01, 7.50000000e-01, 6.11386658e-01, 1.22453853e+00, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 2.23068949e-01}, + {0.00000000e+00, 4.34566961e-01, 2.52789787e-01, 2.46442788e-01, + 1.93031482e-01, 2.83623924e-01, 8.45971180e-01, 1.07004308e-01, + 1.39752446e-01, 1.17505537e+01, 2.66278255e-01, 1.19239531e+00, + 1.36908981e+00, 3.22065793e-01, 1.59577365e-01, 1.89299534e-01, + 3.01479599e-01, 2.53646367e-01, 7.61111058e-01, 3.00374358e+00, + 4.36969076e-02, 7.50000000e-01, 2.80489781e-01, 2.42519143e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 4.37821344e+00}, + {0.00000000e+00, 2.49319770e-01, 7.77906911e-01, 3.83065039e-02, + 4.83846284e-01, 4.66181910e-01, 4.12894272e-02, 2.09861286e-01, + 3.35085062e-01, 2.66278255e-01, 7.61026217e+00, 1.52754534e-01, + 9.47937549e-01, 1.11880255e+00, 2.56118662e-01, 7.21669881e-01, + 1.93666556e+00, 5.20459614e-01, 6.20196625e-01, 1.38845936e-01, + 7.91911207e-02, 7.50000000e-01, 1.00771644e-01, 5.77518724e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 1.87009175e-01}, + {0.00000000e+00, 2.49736666e-01, 1.19715714e-01, 3.01944614e-02, + 6.35580693e-02, 1.17487568e-01, 8.23804865e-01, 8.09786598e-02, + 2.59072609e-01, 1.19239531e+00, 1.52754534e-01, 8.22843955e+00, + 2.12939550e+00, 1.84817584e-01, 1.98778178e-01, 3.50784854e-01, + 1.37359422e-01, 1.48574310e-01, 2.40326213e-01, 9.08401895e-01, + 2.67168548e-01, 7.50000000e-01, 2.38980900e-01, 2.19154102e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 6.10538395e+00}, + {0.00000000e+00, 3.75829845e-01, 1.39730820e-01, 4.42379868e-02, + 1.03502951e-01, 2.12426533e-01, 5.08442478e-01, 1.44276056e-01, + 1.22395235e-01, 1.36908981e+00, 9.47937549e-01, 2.12939550e+00, + 2.85861616e+01, 1.81728699e-01, 1.75926195e-01, 4.86044449e-01, + 4.88080973e-01, 3.18976045e-01, 4.97701201e-01, 1.13284160e+00, + 5.97810639e-02, 7.50000000e-01, 9.73038916e-02, 3.31664034e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 1.89998090e+00}, + {0.00000000e+00, 5.88646912e-01, 4.87904539e+00, 9.65904773e-02, + 2.39946804e+00, 9.64695360e-01, 1.31366508e-01, 6.59774806e-01, + 1.67658945e+00, 3.22065793e-01, 1.11880255e+00, 1.84817584e-01, + 1.81728699e-01, 7.75354485e+00, 3.44509706e-01, 6.36668056e-01, + 3.86583495e-01, 1.36623218e+00, 8.98965212e-01, 1.99138136e-01, + 1.37893708e-01, 7.50000000e-01, 3.89624106e-01, 8.21747280e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 2.26230851e-01}, + {0.00000000e+00, 1.03045017e+00, 2.73736473e-01, 1.58823055e-01, + 2.12686642e-01, 3.47480663e-01, 8.91727264e-02, 3.08399509e-01, + 4.92867985e-01, 1.59577365e-01, 2.56118662e-01, 1.98778178e-01, + 1.75926195e-01, 3.44509706e-01, 1.18714088e+01, 6.84263791e-01, + 5.26778639e-01, 9.79291808e-01, 5.30644206e-01, 3.07279290e-01, + 4.18182164e-02, 7.50000000e-01, 4.66910129e-02, 4.94244365e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 1.86949727e-01}, + {0.00000000e+00, 4.65602795e-01, 7.90138593e-01, 3.93861754e-02, + 9.22524100e-01, 2.25815498e+00, 5.25046736e-02, 2.37177521e-01, + 2.18832010e+00, 1.89299534e-01, 7.21669881e-01, 3.50784854e-01, + 4.86044449e-01, 6.36668056e-01, 6.84263791e-01, 1.14706864e+01, + 1.02361055e+00, 3.73519396e-01, 3.29322200e-01, 2.32053434e-01, + 5.49721446e-02, 7.50000000e-01, 7.38645866e-02, 6.27280154e+00, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 3.02058283e-01}, + {0.00000000e+00, 2.26934456e-01, 2.53425041e-01, 1.51302811e-01, + 1.38560979e-01, 1.78914910e-01, 9.93639027e-02, 1.22086776e-01, + 1.05779530e+00, 3.01479599e-01, 1.93666556e+00, 1.37359422e-01, + 4.88080973e-01, 3.86583495e-01, 5.26778639e-01, 1.02361055e+00, + 1.36584013e+01, 6.10010848e-01, 2.83658630e-01, 1.75829089e-01, + 9.90780192e-01, 7.50000000e-01, 8.59463892e-02, 5.47017256e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 1.86881035e-01}, + {0.00000000e+00, 1.34964157e+00, 9.54703234e-01, 6.65193229e-01, + 5.99713481e-01, 4.78627630e-01, 2.15603624e-01, 9.64187863e-01, + 3.26721114e-01, 2.53646367e-01, 5.20459614e-01, 1.48574310e-01, + 3.18976045e-01, 1.36623218e+00, 9.79291808e-01, 3.73519396e-01, + 6.10010848e-01, 5.20445832e+00, 1.69490837e+00, 3.03324606e-01, + 3.23868004e-01, 7.50000000e-01, 2.08076540e-01, 4.32823454e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 1.80278747e-01}, + {0.00000000e+00, 1.32707297e+00, 6.53688648e-01, 1.86417892e-01, + 4.42110174e-01, 3.38114944e-01, 1.37600314e-01, 3.70424893e-01, + 2.30266854e-01, 7.61111058e-01, 6.20196625e-01, 2.40326213e-01, + 4.97701201e-01, 8.98965212e-01, 5.30644206e-01, 3.29322200e-01, + 2.83658630e-01, 1.69490837e+00, 7.33707214e+00, 7.18561720e-01, + 5.45397612e-02, 7.50000000e-01, 2.27833757e-01, 3.34283233e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 3.97467804e-01}, + {0.00000000e+00, 8.08640731e-01, 1.85063408e-01, 2.74442219e-01, + 1.72922381e-01, 2.46972907e-01, 2.07856659e-01, 3.12986176e-01, + 2.33270842e-01, 3.00374358e+00, 1.38845936e-01, 9.08401895e-01, + 1.13284160e+00, 1.99138136e-01, 3.07279290e-01, 2.32053434e-01, + 1.75829089e-01, 3.03324606e-01, 7.18561720e-01, 8.21110545e+00, + 2.66588959e-02, 7.50000000e-01, 1.80843519e-01, 2.40471284e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 1.54065018e+00}, + {0.00000000e+00, 4.32618091e-02, 7.80441148e-02, 2.22251459e-02, + 2.64171458e-02, 1.89756038e-02, 4.52242030e-01, 2.68996967e-02, + 1.86063506e-01, 4.36969076e-02, 7.91911207e-02, 2.67168548e-01, + 5.97810639e-02, 1.37893708e-01, 4.18182164e-02, 5.49721446e-02, + 9.90780192e-01, 3.23868004e-01, 5.45397612e-02, 2.66588959e-02, + 8.06167129e+01, 7.50000000e-01, 3.76692625e-01, 3.46622138e-02, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 1.99738227e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 1.54222102e-01, 2.23329473e-01, 5.23320532e-01, + 7.98817496e-02, 1.19141830e-01, 3.37849562e+00, 4.37757319e-02, + 6.11386658e-01, 2.80489781e-01, 1.00771644e-01, 2.38980900e-01, + 9.73038916e-02, 3.89624106e-01, 4.66910129e-02, 7.38645866e-02, + 8.59463892e-02, 2.08076540e-01, 2.27833757e-01, 1.80843519e-01, + 3.76692625e-01, 7.50000000e-01, 2.31438270e+01, 9.94108657e-02, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 2.51505787e-01}, + {0.00000000e+00, 6.38034395e-01, 1.61317606e+00, 3.90058338e-02, + 2.29587193e+00, 5.86672974e+00, 4.44561913e-02, 3.80863925e-01, + 1.22453853e+00, 2.42519143e-01, 5.77518724e-01, 2.19154102e-01, + 3.31664034e-01, 8.21747280e-01, 4.94244365e-01, 6.27280154e+00, + 5.47017256e-01, 4.32823454e-01, 3.34283233e-01, 2.40471284e-01, + 3.46622138e-02, 7.50000000e-01, 9.94108657e-02, 6.04368813e+00, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 2.26204268e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 2.00000000e-02, 2.00000000e-02, 2.00000000e-02, + 2.00000000e-02, 2.00000000e-02, 2.00000000e-02, 2.00000000e-02, + 2.00000000e-02, 2.00000000e-02, 2.00000000e-02, 2.00000000e-02, + 2.00000000e-02, 2.00000000e-02, 2.00000000e-02, 2.00000000e-02, + 2.00000000e-02, 2.00000000e-02, 2.00000000e-02, 2.00000000e-02, + 2.00000000e-02, 2.00000000e-02, 2.00000000e-02, 2.00000000e-02, + 2.00000000e-02, 1.33300000e+00, 2.00000000e-02, 2.00000000e-02}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 3.05507349e-01, 1.59869478e-01, 9.54452197e-02, + 1.02625370e-01, 1.67617543e-01, 8.30493328e-01, 8.88316375e-02, + 2.23068949e-01, 4.37821344e+00, 1.87009175e-01, 6.10538395e+00, + 1.89998090e+00, 2.26230851e-01, 1.86949727e-01, 3.02058283e-01, + 1.86881035e-01, 1.80278747e-01, 3.97467804e-01, 1.54065018e+00, + 1.99738227e-01, 7.50000000e-01, 2.51505787e-01, 2.26204268e-01, + 7.50000000e-01, 2.00000000e-02, 7.50000000e-01, 5.58422761e+00}}; + + +/** Underlying frequency ratios for PAM250 */ +static const double PAM250_FREQRATIOS[BLASTAA_SIZE][BLASTAA_SIZE] = +{{0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00}, + {0.00000000e+00, 1.51578006e+00, 1.05606466e+00, 6.26687137e-01, + 1.07002228e+00, 1.07474947e+00, 4.46290560e-01, 1.33923306e+00, + 7.32178470e-01, 8.88728816e-01, 7.66227814e-01, 6.46341735e-01, + 7.70359541e-01, 1.03988401e+00, 1.29408826e+00, 9.03456780e-01, + 7.00798809e-01, 1.28966428e+00, 1.31725380e+00, 1.04499203e+00, + 2.63609841e-01, 7.50000000e-01, 4.49686798e-01, 1.00010336e+00, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 7.19479599e-01}, + {0.00000000e+00, 1.05606466e+00, 1.84257846e+00, 3.65113184e-01, + 2.05195422e+00, 1.82470118e+00, 3.54071966e-01, 1.11918465e+00, + 1.29683470e+00, 6.16970770e-01, 1.13010166e+00, 4.52559218e-01, + 6.04775077e-01, 1.59985542e+00, 8.46142888e-01, 1.33890747e+00, + 8.61808201e-01, 1.11706882e+00, 1.03197349e+00, 6.37811920e-01, + 2.96793416e-01, 7.50000000e-01, 4.85608019e-01, 1.61300149e+00, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 5.02168751e-01}, + {0.00000000e+00, 6.26687137e-01, 3.65113184e-01, 1.56285624e+01, + 3.06772262e-01, 2.94964746e-01, 3.70976416e-01, 4.59297624e-01, + 4.51836855e-01, 5.87603719e-01, 2.86099293e-01, 2.47791243e-01, + 2.99408166e-01, 4.32746060e-01, 5.27421501e-01, 2.89534285e-01, + 4.30504374e-01, 9.89678273e-01, 6.01385571e-01, 6.38902701e-01, + 1.68818010e-01, 7.50000000e-01, 1.07830459e+00, 2.92598254e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 3.50326241e-01}, + {0.00000000e+00, 1.07002228e+00, 2.05195422e+00, 3.06772262e-01, + 2.43292288e+00, 2.19715052e+00, 2.73911818e-01, 1.14726861e+00, + 1.17343465e+00, 5.79909835e-01, 1.01987501e+00, 3.98183329e-01, + 5.48083233e-01, 1.61030872e+00, 8.05577240e-01, 1.46152937e+00, + 7.42874842e-01, 1.06886174e+00, 9.67471640e-01, 6.11601820e-01, + 2.12712296e-01, 7.50000000e-01, 3.69025897e-01, 1.87658077e+00, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 4.53017475e-01}, + {0.00000000e+00, 1.07474947e+00, 1.82470118e+00, 2.94964746e-01, + 2.19715052e+00, 2.41404373e+00, 2.87098831e-01, 1.04389169e+00, + 1.16525001e+00, 6.26339428e-01, 9.87756703e-01, 4.63568053e-01, + 6.13603630e-01, 1.39293185e+00, 8.79669521e-01, 1.77158803e+00, + 7.82149443e-01, 9.98597151e-01, 9.13853001e-01, 6.60440693e-01, + 2.00637717e-01, 7.50000000e-01, 3.71943861e-01, 2.13407372e+00, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 5.12682678e-01}, + {0.00000000e+00, 4.46290560e-01, 3.54071966e-01, 3.70976416e-01, + 2.73911818e-01, 2.87098831e-01, 8.02764197e+00, 3.32339414e-01, + 6.59545216e-01, 1.26203255e+00, 2.98137999e-01, 1.51752623e+00, + 1.04274830e+00, 4.46999215e-01, 3.50828021e-01, 3.43119584e-01, + 3.58749728e-01, 4.78353203e-01, 4.88459165e-01, 7.66309017e-01, + 1.07668873e+00, 7.50000000e-01, 4.97590308e+00, 3.11511613e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 1.44043358e+00}, + {0.00000000e+00, 1.33923306e+00, 1.11918465e+00, 4.59297624e-01, + 1.14726861e+00, 1.04389169e+00, 3.32339414e-01, 2.99056680e+00, + 6.15552047e-01, 5.57103314e-01, 6.78509416e-01, 3.94686522e-01, + 5.25369171e-01, 1.08662777e+00, 8.94077901e-01, 7.57447784e-01, + 5.54986868e-01, 1.27845679e+00, 9.98770702e-01, 7.34634729e-01, + 2.00363838e-01, 7.50000000e-01, 2.99613619e-01, 9.19064864e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 4.43694156e-01}, + {0.00000000e+00, 7.32178470e-01, 1.29683470e+00, 4.51836855e-01, + 1.17343465e+00, 1.16525001e+00, 6.59545216e-01, 6.15552047e-01, + 4.47513035e+00, 5.70614299e-01, 9.90160946e-01, 6.20055020e-01, + 6.09441413e-01, 1.43988866e+00, 9.46471632e-01, 1.96194975e+00, + 1.43036819e+00, 8.30226277e-01, 7.40733381e-01, 5.96692220e-01, + 5.44285116e-01, 7.50000000e-01, 9.78877039e-01, 1.51243666e+00, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 6.05136779e-01}, + {0.00000000e+00, 8.88728816e-01, 6.16970770e-01, 5.87603719e-01, + 5.79909835e-01, 6.26339428e-01, 1.26203255e+00, 5.57103314e-01, + 5.70614299e-01, 2.83022396e+00, 6.40800116e-01, 1.74919442e+00, + 1.64986005e+00, 6.59934398e-01, 6.27243837e-01, 6.25316723e-01, + 6.29014923e-01, 7.22513707e-01, 1.01665478e+00, 2.33821719e+00, + 3.02670201e-01, 7.50000000e-01, 7.99863687e-01, 6.25893752e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 2.07538421e+00}, + {0.00000000e+00, 7.66227814e-01, 1.13010166e+00, 2.86099293e-01, + 1.01987501e+00, 9.87756703e-01, 2.98137999e-01, 6.78509416e-01, + 9.90160946e-01, 6.40800116e-01, 2.92620120e+00, 5.18814877e-01, + 1.09961986e+00, 1.25788411e+00, 7.70516817e-01, 1.18387275e+00, + 2.18168472e+00, 9.61645918e-01, 9.95647751e-01, 5.70096500e-01, + 4.50323287e-01, 7.50000000e-01, 3.59829368e-01, 1.07322036e+00, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 5.55622697e-01}, + {0.00000000e+00, 6.46341735e-01, 4.52559218e-01, 2.47791243e-01, + 3.98183329e-01, 4.63568053e-01, 1.51752623e+00, 3.94686522e-01, + 6.20055020e-01, 1.74919442e+00, 5.18814877e-01, 3.92994485e+00, + 2.33771776e+00, 5.15595552e-01, 5.56038228e-01, 6.65507525e-01, + 5.01008003e-01, 5.24107036e-01, 6.77040993e-01, 1.53178711e+00, + 6.46886104e-01, 7.50000000e-01, 8.14904569e-01, 5.51569446e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 3.27192533e+00}, + {0.00000000e+00, 7.70359541e-01, 6.04775077e-01, 2.99408166e-01, + 5.48083233e-01, 6.13603630e-01, 1.04274830e+00, 5.25369171e-01, + 6.09441413e-01, 1.64986005e+00, 1.09961986e+00, 2.33771776e+00, + 4.40351686e+00, 6.70496227e-01, 6.21012433e-01, 7.95710012e-01, + 9.04471250e-01, 6.98581457e-01, 8.73067477e-01, 1.51039751e+00, + 3.74941590e-01, 7.50000000e-01, 5.67675865e-01, 6.92962139e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 2.13016362e+00}, + {0.00000000e+00, 1.03988401e+00, 1.59985542e+00, 4.32746060e-01, + 1.61030872e+00, 1.39293185e+00, 4.46999215e-01, 1.08662777e+00, + 1.43988866e+00, 6.59934398e-01, 1.25788411e+00, 5.15595552e-01, + 6.70496227e-01, 1.58773724e+00, 8.93169424e-01, 1.19675558e+00, + 9.99684066e-01, 1.17295383e+00, 1.10674855e+00, 6.68196499e-01, + 3.94266130e-01, 7.50000000e-01, 6.20758168e-01, 1.30744195e+00, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 5.59148347e-01}, + {0.00000000e+00, 1.29408826e+00, 8.46142888e-01, 5.27421501e-01, + 8.05577240e-01, 8.79669521e-01, 3.50828021e-01, 8.94077901e-01, + 9.46471632e-01, 6.27243837e-01, 7.70516817e-01, 5.56038228e-01, + 6.21012433e-01, 8.93169424e-01, 3.84141803e+00, 1.05550534e+00, + 9.60208581e-01, 1.24349521e+00, 1.07552180e+00, 7.58279920e-01, + 2.75343404e-01, 7.50000000e-01, 3.20901839e-01, 9.56295438e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 5.77523805e-01}, + {0.00000000e+00, 9.03456780e-01, 1.33890747e+00, 2.89534285e-01, + 1.46152937e+00, 1.77158803e+00, 3.43119584e-01, 7.57447784e-01, + 1.96194975e+00, 6.25316723e-01, 1.18387275e+00, 6.65507525e-01, + 7.95710012e-01, 1.19675558e+00, 1.05550534e+00, 2.54088209e+00, + 1.33504131e+00, 8.89983529e-01, 8.32948914e-01, 6.49448913e-01, + 3.35176014e-01, 7.50000000e-01, 3.94549334e-01, 2.10683180e+00, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 6.53380354e-01}, + {0.00000000e+00, 7.00798809e-01, 8.61808201e-01, 4.30504374e-01, + 7.42874842e-01, 7.82149443e-01, 3.58749728e-01, 5.54986868e-01, + 1.43036819e+00, 6.29014923e-01, 2.18168472e+00, 5.01008003e-01, + 9.04471250e-01, 9.99684066e-01, 9.60208581e-01, 1.33504131e+00, + 4.07760419e+00, 9.28286966e-01, 8.20208528e-01, 5.61449723e-01, + 1.65111328e+00, 7.50000000e-01, 3.78768704e-01, 1.02308924e+00, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 5.39632804e-01}, + {0.00000000e+00, 1.28966428e+00, 1.11706882e+00, 9.89678273e-01, + 1.06886174e+00, 9.98597151e-01, 4.78353203e-01, 1.27845679e+00, + 8.30226277e-01, 7.22513707e-01, 9.61645918e-01, 5.24107036e-01, + 6.98581457e-01, 1.17295383e+00, 1.24349521e+00, 8.89983529e-01, + 9.28286966e-01, 1.44062735e+00, 1.36207698e+00, 7.99071364e-01, + 5.65535441e-01, 7.50000000e-01, 5.19882078e-01, 9.51265394e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 5.83974254e-01}, + {0.00000000e+00, 1.31725380e+00, 1.03197349e+00, 6.01385571e-01, + 9.67471640e-01, 9.13853001e-01, 4.88459165e-01, 9.98770702e-01, + 7.40733381e-01, 1.01665478e+00, 9.95647751e-01, 6.77040993e-01, + 8.73067477e-01, 1.10674855e+00, 1.07552180e+00, 8.32948914e-01, + 8.20208528e-01, 1.36207698e+00, 1.80640078e+00, 1.06764169e+00, + 3.05841935e-01, 7.50000000e-01, 5.30232118e-01, 8.78596534e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 7.79516037e-01}, + {0.00000000e+00, 1.04499203e+00, 6.37811920e-01, 6.38902701e-01, + 6.11601820e-01, 6.60440693e-01, 7.66309017e-01, 7.34634729e-01, + 5.96692220e-01, 2.33821719e+00, 5.70096500e-01, 1.53178711e+00, + 1.51039751e+00, 6.68196499e-01, 7.58279920e-01, 6.49448913e-01, + 5.61449723e-01, 7.99071364e-01, 1.06764169e+00, 2.69825619e+00, + 2.36794202e-01, 7.50000000e-01, 5.65836205e-01, 6.55650683e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 1.77511928e+00}, + {0.00000000e+00, 2.63609841e-01, 2.96793416e-01, 1.68818010e-01, + 2.12712296e-01, 2.00637717e-01, 1.07668873e+00, 2.00363838e-01, + 5.44285116e-01, 3.02670201e-01, 4.50323287e-01, 6.46886104e-01, + 3.74941590e-01, 3.94266130e-01, 2.75343404e-01, 3.35176014e-01, + 1.65111328e+00, 5.65535441e-01, 3.05841935e-01, 2.36794202e-01, + 5.26606678e+01, 7.50000000e-01, 9.69642510e-01, 2.59266956e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 5.43022417e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 4.49686798e-01, 4.85608019e-01, 1.07830459e+00, + 3.69025897e-01, 3.71943861e-01, 4.97590308e+00, 2.99613619e-01, + 9.78877039e-01, 7.99863687e-01, 3.59829368e-01, 8.14904569e-01, + 5.67675865e-01, 6.20758168e-01, 3.20901839e-01, 3.94549334e-01, + 3.78768704e-01, 5.19882078e-01, 5.30232118e-01, 5.65836205e-01, + 9.69642510e-01, 7.50000000e-01, 1.03387565e+01, 3.81794898e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 8.10366134e-01}, + {0.00000000e+00, 1.00010336e+00, 1.61300149e+00, 2.92598254e-01, + 1.87658077e+00, 2.13407372e+00, 3.11511613e-01, 9.19064864e-01, + 1.51243666e+00, 6.25893752e-01, 1.07322036e+00, 5.51569446e-01, + 6.92962139e-01, 1.30744195e+00, 9.56295438e-01, 2.10683180e+00, + 1.02308924e+00, 9.51265394e-01, 8.78596534e-01, 6.55650683e-01, + 2.59266956e-01, 7.50000000e-01, 3.81794898e-01, 2.12220220e+00, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 5.73996058e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 1.70000000e-01, 1.70000000e-01, 1.70000000e-01, + 1.70000000e-01, 1.70000000e-01, 1.70000000e-01, 1.70000000e-01, + 1.70000000e-01, 1.70000000e-01, 1.70000000e-01, 1.70000000e-01, + 1.70000000e-01, 1.70000000e-01, 1.70000000e-01, 1.70000000e-01, + 1.70000000e-01, 1.70000000e-01, 1.70000000e-01, 1.70000000e-01, + 1.70000000e-01, 1.70000000e-01, 1.70000000e-01, 1.70000000e-01, + 1.70000000e-01, 1.33300000e+00, 1.70000000e-01, 1.70000000e-01}, + {0.00000000e+00, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, 7.50000000e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 7.50000000e-01}, + {0.00000000e+00, 7.19479599e-01, 5.02168751e-01, 3.50326241e-01, + 4.53017475e-01, 5.12682678e-01, 1.44043358e+00, 4.43694156e-01, + 6.05136779e-01, 2.07538421e+00, 5.55622697e-01, 3.27192533e+00, + 2.13016362e+00, 5.59148347e-01, 5.77523805e-01, 6.53380354e-01, + 5.39632804e-01, 5.83974254e-01, 7.79516037e-01, 1.77511928e+00, + 5.43022417e-01, 7.50000000e-01, 8.10366134e-01, 5.73996058e-01, + 7.50000000e-01, 1.70000000e-01, 7.50000000e-01, 2.91088108e+00}}; + + +/** Additional bit scale multiplier for BLOSUM62_20A matrix */ +#define BLOSUM62_20A_SCALE_MULTIPLIER 0.9666 +/** Additional bit scale multiplier for BLOSUM62_20B matrix */ +#define BLOSUM62_20B_SCALE_MULTIPLIER 0.9344 + +SFreqRatios* +_PSIMatrixFrequencyRatiosNew(const char* matrix_name) +{ + unsigned int i, j; /* loop indices */ + SFreqRatios* retval = NULL; /* the return value */ + + ASSERT(matrix_name); + + retval = (SFreqRatios*) malloc(sizeof(SFreqRatios)); + if ( !retval ) { + return NULL; + } + + retval->data = (double**) _PSIAllocateMatrix(BLASTAA_SIZE, BLASTAA_SIZE, + sizeof(double)); + if ( !retval->data ) { + return _PSIMatrixFrequencyRatiosFree(retval); + } + + if ( !strcasecmp(matrix_name, "BLOSUM62") || + !strcasecmp(matrix_name, "BLOSUM62_20")) { + for (i = 0; i < BLASTAA_SIZE; i++) { + for (j = 0; j < BLASTAA_SIZE; j++) { + retval->data[i][j] = BLOSUM62_FREQRATIOS[i][j]; + } + } + retval->bit_scale_factor = 2; + } else if ( !strcasecmp(matrix_name, "BLOSUM62_20A")) { + for (i = 0; i < BLASTAA_SIZE; i++) { + for (j = 0; j < BLASTAA_SIZE; j++) { + retval->data[i][j] = + BLOSUM62_20A_SCALE_MULTIPLIER * BLOSUM62_FREQRATIOS[i][j]; + } + } + retval->bit_scale_factor = 2; + } else if ( !strcasecmp(matrix_name, "BLOSUM62_20B")) { + for (i = 0; i < BLASTAA_SIZE; i++) { + for (j = 0; j < BLASTAA_SIZE; j++) { + retval->data[i][j] = + BLOSUM62_20B_SCALE_MULTIPLIER * BLOSUM62_FREQRATIOS[i][j]; + } + } + retval->bit_scale_factor = 2; + } else if ( !strcasecmp(matrix_name, "BLOSUM45") ) { + for (i = 0; i < BLASTAA_SIZE; i++) { + for (j = 0; j < BLASTAA_SIZE; j++) { + retval->data[i][j] = BLOSUM45_FREQRATIOS[i][j]; + } + } + retval->bit_scale_factor = 3; + } else if ( !strcasecmp(matrix_name, "BLOSUM80") ) { + for (i = 0; i < BLASTAA_SIZE; i++) { + for (j = 0; j < BLASTAA_SIZE; j++) { + retval->data[i][j] = BLOSUM80_FREQRATIOS[i][j]; + } + } + retval->bit_scale_factor = 2; + } else if ( !strcasecmp(matrix_name, "BLOSUM50") ) { + for (i = 0; i < BLASTAA_SIZE; i++) { + for (j = 0; j < BLASTAA_SIZE; j++) { + retval->data[i][j] = BLOSUM50_FREQRATIOS[i][j]; + } + } + retval->bit_scale_factor = 2; + } else if ( !strcasecmp(matrix_name, "BLOSUM90") ) { + for (i = 0; i < BLASTAA_SIZE; i++) { + for (j = 0; j < BLASTAA_SIZE; j++) { + retval->data[i][j] = BLOSUM90_FREQRATIOS[i][j]; + } + } + retval->bit_scale_factor = 2; + } else if ( !strcasecmp(matrix_name, "PAM30") ) { + for (i = 0; i < BLASTAA_SIZE; i++) { + for (j = 0; j < BLASTAA_SIZE; j++) { + retval->data[i][j] = PAM30_FREQRATIOS[i][j]; + } + } + retval->bit_scale_factor = 2; + } else if ( !strcasecmp(matrix_name, "PAM70") ) { + for (i = 0; i < BLASTAA_SIZE; i++) { + for (j = 0; j < BLASTAA_SIZE; j++) { + retval->data[i][j] = PAM70_FREQRATIOS[i][j]; + } + } + retval->bit_scale_factor = 2; + } else if ( !strcasecmp(matrix_name, "PAM250") ) { + for (i = 0; i < BLASTAA_SIZE; i++) { + for (j = 0; j < BLASTAA_SIZE; j++) { + retval->data[i][j] = PAM250_FREQRATIOS[i][j]; + } + } + retval->bit_scale_factor = 2; + } else { + retval = _PSIMatrixFrequencyRatiosFree(retval); + } + + return retval; +} + +SFreqRatios* +_PSIMatrixFrequencyRatiosFree(SFreqRatios* freq_ratios) +{ + if ( !freq_ratios ) + return NULL; + + if (freq_ratios->data) { + _PSIDeallocateMatrix((void**) freq_ratios->data, BLASTAA_SIZE); + } + + sfree(freq_ratios); + return NULL; +} diff --git a/src/lib/blast/matrix_freq_ratios.h b/src/lib/blast/matrix_freq_ratios.h new file mode 100644 index 000000000..864214374 --- /dev/null +++ b/src/lib/blast/matrix_freq_ratios.h @@ -0,0 +1,83 @@ +#ifndef ALGO_BLAST_CORE___MATRIX_FREQ_RATIOS__H +#define ALGO_BLAST_CORE___MATRIX_FREQ_RATIOS__H + +/* $Id$ + * =========================================================================== + * + * PUBLIC DOMAIN NOTICE + * National Center for Biotechnology Information + * + * This software/database is a "United States Government Work" under the + * terms of the United States Copyright Act. It was written as part of + * the author's official duties as a United States Government employee and + * thus cannot be copyrighted. This software/database is freely available + * to the public for use. The National Library of Medicine and the U.S. + * Government have not placed any restriction on its use or reproduction. + * + * Although all reasonable efforts have been taken to ensure the accuracy + * and reliability of the software and data, the NLM and the U.S. + * Government do not and cannot warrant the performance or results that + * may be obtained by using this software or data. The NLM and the U.S. + * Government disclaim all warranties, express or implied, including + * warranties of performance, merchantability or fitness for any particular + * purpose. + * + * Please cite the author in any work or product based on this material. + * + * =========================================================================== + * + * Author: Christiam Camacho + * + */ + +/** @file matrix_freq_ratios.h + * Interface to retrieve the frequency ratios for various scoring matrices. + * + * See explanation in p 2996 of Nucleic Acids Research, 2001, Vol 29, No 14. + */ + +#include "blast_encoding.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Stores the frequency ratios along with their bit scale factor */ +typedef struct SFreqRatios { + + /** The actual frequency ratios */ + double** data; + + /** Used to multiply the values in the above matrix to obtain scores in bit + * units */ + int bit_scale_factor; + +} SFreqRatios; + +/** Retrive the matrix's frequency ratios. + * @param matrix_name Available options include: + * BLOSUM62 + * BLOSUM62_20 + * BLOSUM62_20A + * BLOSUM62_20B + * BLOSUM45 + * BLOSUM80 + * BLOSUM50 + * BLOSUM90 + * PAM30 + * PAM70 + * PAM250 + * @return NULL on error + */ +NCBI_XBLAST_EXPORT SFreqRatios* +_PSIMatrixFrequencyRatiosNew(const char* matrix_name); + +/** Deallocate the frequency ratios structure */ +NCBI_XBLAST_EXPORT SFreqRatios* +_PSIMatrixFrequencyRatiosFree(SFreqRatios* freq_ratios); + +#ifdef __cplusplus +} +#endif + +#endif /* !ALGO_BLAST_CORE__MATRIX_FREQ_RATIOS__H */ diff --git a/src/lib/blast/ncbi_std.cpp b/src/lib/blast/ncbi_std.cpp new file mode 100644 index 000000000..1bd879e22 --- /dev/null +++ b/src/lib/blast/ncbi_std.cpp @@ -0,0 +1,182 @@ +/* $Id$ + * =========================================================================== + * + * PUBLIC DOMAIN NOTICE + * National Center for Biotechnology Information + * + * This software/database is a "United States Government Work" under the + * terms of the United States Copyright Act. It was written as part of + * the author's official duties as a United States Government employee and + * thus cannot be copyrighted. This software/database is freely available + * to the public for use. The National Library of Medicine and the U.S. + * Government have not placed any restriction on its use or reproduction. + * + * Although all reasonable efforts have been taken to ensure the accuracy + * and reliability of the software and data, the NLM and the U.S. + * Government do not and cannot warrant the performance or results that + * may be obtained by using this software or data. The NLM and the U.S. + * Government disclaim all warranties, express or implied, including + * warranties of performance, merchantability or fitness for any particular + * purpose. + * + * Please cite the author in any work or product based on this material. + * + * =========================================================================== + * + */ + +/** @file ncbi_std.c + * A few utilities needed by code in algo/blast/core but not provided elsewhere. + */ + +#include "blast_def.h" /* for sfree() macro */ +#include "ncbi_std.h" + +void * BlastMemDup (const void *orig, size_t size) +{ + void* copy; + + if (orig == NULL || size == 0) + return NULL; + + if ((copy = malloc (size)) == NULL) + return NULL; + + memcpy(copy, orig, size); + return copy; +} + +/***************************************************************************** +* +* ListNodeNew(vnp) +* adds after last node in list if vnp not NULL +* +*****************************************************************************/ +ListNode* ListNodeNew (ListNode* vnp) +{ + ListNode* newnode; + + newnode = (ListNode*) calloc(1, sizeof(ListNode)); + if (vnp != NULL) + { + while (vnp->next != NULL) + vnp = vnp->next; + vnp->next = newnode; + } + return newnode; +} + +/***************************************************************************** +* +* ListNodeAdd(head) +* adds after last node in list if *head not NULL +* If *head is NULL, sets it to the new ListNode +* returns pointer to the NEW node added +* +*****************************************************************************/ +ListNode* ListNodeAdd (ListNode** head) +{ + ListNode* newnode; + + if (head != NULL) + { + newnode = ListNodeNew(*head); + if (*head == NULL) + *head = newnode; + } + else + newnode = ListNodeNew(NULL); + + return newnode; +} + +/* ListNodeAddPointer (head, choice, value) +* adds like ListNodeAdd() +* sets newnode->choice = choice (if choice does not matter, use 0) +* sets newnode->ptr = value +* +*****************************************************************************/ +ListNode* ListNodeAddPointer (ListNode** head, unsigned choice, + void *value) +{ + ListNode* newnode; + + newnode = ListNodeAdd(head); + if (newnode != NULL) + { + newnode->choice = choice; + newnode->ptr = value; + } + + return newnode; +} + +/***************************************************************************** +* +* ListNodeCopyStr (head, choice, str) +* adds like ListNodeAdd() +* sets newnode->choice = choice (if choice does not matter, use 0) +* sets newnode->ptr = str +* makes a COPY of str +* if str == NULL, does not add a ListNode +* +*****************************************************************************/ +ListNode* ListNodeCopyStr (ListNode** head, int choice, const char* str) +{ + ListNode* newnode; + + if (str == NULL) return NULL; + + newnode = ListNodeAdd(head); + if (newnode != NULL) + { + newnode->choice = choice; + newnode->ptr = strdup(str); + } + + return newnode; +} + +/***************************************************************************** +* +* ListNodeFree(vnp) +* frees whole chain of ListNodes +* Does NOT free associated data pointers +* see ListNodeFreeData() +* +*****************************************************************************/ +ListNode* ListNodeFree (ListNode* vnp) +{ + ListNode* next; + + while (vnp != NULL) + { + next = vnp->next; + sfree(vnp); + vnp = next; + } + return NULL; +} + +/***************************************************************************** +* +* ListNodeFreeData(vnp) +* frees whole chain of ListNodes +* frees associated data pointers - BEWARE of this if these are not +* allocated single memory block structures. +* +*****************************************************************************/ +ListNode* ListNodeFreeData (ListNode* vnp) +{ + ListNode* next; + + while (vnp != NULL) + { + sfree(vnp->ptr); + next = vnp->next; + sfree(vnp); + vnp = next; + } + return NULL; +} + diff --git a/src/lib/blast/ncbi_std.h b/src/lib/blast/ncbi_std.h index fbac29a2a..63c0a7199 100644 --- a/src/lib/blast/ncbi_std.h +++ b/src/lib/blast/ncbi_std.h @@ -208,7 +208,7 @@ void* BlastMemDup (const void *orig, size_t size); /** A generic linked list node structure */ typedef struct ListNode { - Uint1 choice; /**< to pick a choice */ + int choice; /**< to pick a choice */ void *ptr; /**< attached data */ struct ListNode *next; /**< next in linked list */ } ListNode; @@ -236,7 +236,7 @@ ListNode* ListNodeAdd (ListNode** head); * @return New node */ NCBI_XBLAST_EXPORT -ListNode* ListNodeAddPointer (ListNode** head, Uint1 choice, void *value); +ListNode* ListNodeAddPointer (ListNode** head, int choice, void *value); /** Free all list's nodes, does not attempt to free data. * @param vnp objects to be freed [in] @@ -252,6 +252,8 @@ ListNode* ListNodeFree (ListNode* vnp); NCBI_XBLAST_EXPORT ListNode* ListNodeFreeData (ListNode* vnp); +ListNode* ListNodeCopyStr (ListNode** head, int choice, const char* str); + /** Add a node to the list with a provided choice, and attached data * pointing to a provided string. * @param head Pointer to the start of the list, if *head is NULL will @@ -261,7 +263,6 @@ ListNode* ListNodeFreeData (ListNode* vnp); * @return newly allocated node */ NCBI_XBLAST_EXPORT -ListNode* ListNodeCopyStr (ListNode** head, Uint1 choice, const char* str); #ifdef __cplusplus } diff --git a/src/lib/cgranges/IITree.h b/src/lib/cgranges/IITree.h new file mode 100644 index 000000000..59faa849a --- /dev/null +++ b/src/lib/cgranges/IITree.h @@ -0,0 +1,110 @@ +#pragma once + +#include +#include + +/* Suppose there are N=2^(K+1)-1 sorted numbers in an array a[]. They + * implicitly form a complete binary tree of height K+1. We consider leaves to + * be at level 0. The binary tree has the following properties: + * + * 1. The lowest k-1 bits of nodes at level k are all 1. The k-th bit is 0. + * The first node at level k is indexed by 2^k-1. The root of the tree is + * indexed by 2^K-1. + * + * 2. For a node x at level k, its left child is x-2^(k-1) and the right child + * is x+2^(k-1). + * + * 3. For a node x at level k, it is a left child if its (k+1)-th bit is 0. Its + * parent node is x+2^k. Similarly, if the (k+1)-th bit is 1, x is a right + * child and its parent is x-2^k. + * + * 4. For a node x at level k, there are 2^(k+1)-1 nodes in the subtree + * descending from x, including x. The left-most leaf is x&~(2^k-1) (masking + * the lowest k bits to 0). + * + * When numbers can't fill a complete binary tree, the parent of a node may not + * be present in the array. The implementation here still mimics a complete + * tree, though getting the special casing right is a little complex. There may + * be alternative solutions. + * + * As a sorted array can be considered as a binary search tree, we can + * implement an interval tree on top of the idea. We only need to record, for + * each node, the maximum value in the subtree descending from the node. + */ +template // "S" is a scalar type; "T" is the type of data associated with each interval +class IITree { + struct StackCell { + size_t x; // node + int k, w; // k: level; w: 0 if left child hasn't been processed + StackCell() {}; + StackCell(int k_, size_t x_, int w_) : x(x_), k(k_), w(w_) {}; + }; + struct Interval { + S st, en, max; + T data; + Interval(const S &s, const S &e, const T &d) : st(s), en(e), max(e), data(d) {}; + }; + struct IntervalLess { + bool operator()(const Interval &a, const Interval &b) const { return a.st < b.st; } + }; + std::vector a; + int max_level; + int index_core(std::vector &a) { + size_t i, last_i; // last_i points to the rightmost node in the tree + S last; // last is the max value at node last_i + int k; + if (a.size() == 0) return -1; + for (i = 0; i < a.size(); i += 2) last_i = i, last = a[i].max = a[i].en; // leaves (i.e. at level 0) + for (k = 1; 1LL< el? e : el; + e = e > er? e : er; + a[i].max = e; // set the max value for node i + } + last_i = last_i>>k&1? last_i - x : last_i + x; // last_i now points to the parent of the original last_i + if (last_i < a.size() && a[last_i].max > last) // update last accordingly + last = a[last_i].max; + } + return k - 1; + } +public: + void add(const S &s, const S &e, const T &d) { a.push_back(Interval(s, e, d)); } + void index(void) { + std::sort(a.begin(), a.end(), IntervalLess()); + max_level = index_core(a); + } + bool overlap(const S &st, const S &en, std::vector &out) const { + int t = 0; + StackCell stack[64]; + out.clear(); + if (max_level < 0) return false; + stack[t++] = StackCell(max_level, (1LL<> z.k << z.k, i1 = i0 + (1LL<<(z.k+1)) - 1; + if (i1 >= a.size()) i1 = a.size(); + for (i = i0; i < i1 && a[i].st < en; ++i) + if (st < a[i].en) // if overlap, append to out[] + out.push_back(i); + } else if (z.w == 0) { // if left child not processed + size_t y = z.x - (1LL<<(z.k-1)); // the left child of z.x; NB: y may be out of range (i.e. y>=a.size()) + stack[t++] = StackCell(z.k, z.x, 1); // re-add node z.x, but mark the left child having been processed + if (y >= a.size() || a[y].max > st) // push the left child if y is out of range or may overlap with the query + stack[t++] = StackCell(z.k - 1, y, 0); + } else if (z.x < a.size() && a[z.x].st < en) { // need to push the right child + if (st < a[z.x].en) out.push_back(z.x); // test if z.x overlaps the query; if yes, append to out[] + stack[t++] = StackCell(z.k - 1, z.x + (1LL<<(z.k-1)), 0); // push the right child + } + } + return out.size() > 0? true : false; + } + size_t size(void) const { return a.size(); } + const S &start(size_t i) const { return a[i].st; } + const S &end(size_t i) const { return a[i].en; } + const T &data(size_t i) const { return a[i].data; } +}; diff --git a/src/lib/gsl/gsl.h b/src/lib/gsl/gsl.h new file mode 100644 index 000000000..33d887318 --- /dev/null +++ b/src/lib/gsl/gsl.h @@ -0,0 +1,407 @@ +#pragma once +#define _USE_MATH_DEFINES +#include + +#define GSL_DBL_EPSILON 2.2204460492503131e-16 +#define GSL_ROOT6_DBL_EPSILON 2.4607833005759251e-03 +#define M_SQRTPI 1.77245385090551602729816748334 /* sqrt(pi) */ + +enum { + GSL_SUCCESS = 0, + GSL_FAILURE = -1, + GSL_CONTINUE = -2, /* iteration has not converged */ + GSL_EDOM = 1, /* input domain error, e.g sqrt(-1) */ + GSL_ERANGE = 2, /* output range error, e.g. exp(1e100) */ + GSL_EFAULT = 3, /* invalid pointer */ + GSL_EINVAL = 4, /* invalid argument supplied by user */ + GSL_EFAILED = 5, /* generic failure */ + GSL_EFACTOR = 6, /* factorization failed */ + GSL_ESANITY = 7, /* sanity check failed - shouldn't happen */ + GSL_ENOMEM = 8, /* malloc failed */ + GSL_EBADFUNC = 9, /* problem with user-supplied function */ + GSL_ERUNAWAY = 10, /* iterative process is out of control */ + GSL_EMAXITER = 11, /* exceeded max number of iterations */ + GSL_EZERODIV = 12, /* tried to divide by zero */ + GSL_EBADTOL = 13, /* user specified an invalid tolerance */ + GSL_ETOL = 14, /* failed to reach the specified tolerance */ + GSL_EUNDRFLW = 15, /* underflow */ + GSL_EOVRFLW = 16, /* overflow */ + GSL_ELOSS = 17, /* loss of accuracy */ + GSL_EROUND = 18, /* failed because of roundoff error */ + GSL_EBADLEN = 19, /* matrix, vector lengths are not conformant */ + GSL_ENOTSQR = 20, /* matrix not square */ + GSL_ESING = 21, /* apparent singularity detected */ + GSL_EDIVERGE = 22, /* integral or series is divergent */ + GSL_EUNSUP = 23, /* requested feature is not supported by the hardware */ + GSL_EUNIMPL = 24, /* requested feature not (yet) implemented */ + GSL_ECACHE = 25, /* cache limit exceeded */ + GSL_ETABLE = 26, /* table limit exceeded */ + GSL_ENOPROG = 27, /* iteration is not making progress towards solution */ + GSL_ENOPROGJ = 28, /* jacobian evaluations are not improving the solution */ + GSL_ETOLF = 29, /* cannot reach the specified tolerance in F */ + GSL_ETOLX = 30, /* cannot reach the specified tolerance in X */ + GSL_ETOLG = 31, /* cannot reach the specified tolerance in gradient */ + GSL_EOF = 32 /* end of file */ +}; + +void +gsl_stream_printf(const char* label, const char* file, int line, + const char* reason) +{ + fprintf(stderr, "gsl: %s:%d: %s: %s\n", file, line, label, reason); +} + +void +gsl_error(const char* reason, const char* file, int line, int gsl_errno) +{ + gsl_stream_printf("ERROR", file, line, reason); + + fflush(stdout); + fprintf(stderr, "Default GSL error handler invoked.\n"); + fflush(stderr); + + abort(); +} + +#define GSL_ERROR_VAL(reason, gsl_errno, value) \ + do { \ + gsl_error (reason, __FILE__, __LINE__, gsl_errno) ; \ + return value ; \ + } while (0) + +#define EVAL_RESULT(fn) \ + gsl_sf_result result; \ + int status = fn; \ + if (status != GSL_SUCCESS) { \ + GSL_ERROR_VAL(#fn, status, result.val); \ + } ; \ + return result.val; + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + +__BEGIN_DECLS + +struct gsl_sf_result_struct { + double val; + double err; +}; +typedef struct gsl_sf_result_struct gsl_sf_result; + +#define GSL_SF_RESULT_SET(r,v,e) do { (r)->val=(v); (r)->err=(e); } while(0) + + +struct gsl_sf_result_e10_struct { + double val; + double err; + int e10; +}; +typedef struct gsl_sf_result_e10_struct gsl_sf_result_e10; + +__END_DECLS + +struct cheb_series_struct { + double* c; /* coefficients */ + int order; /* order of expansion */ + double a; /* lower interval point */ + double b; /* upper interval point */ + int order_sp; /* effective single precision order */ +}; +typedef struct cheb_series_struct cheb_series; + +static inline int +cheb_eval_e(const cheb_series* cs, + const double x, + gsl_sf_result* result) +{ + int j; + double d = 0.0; + double dd = 0.0; + + double y = (2.0 * x - cs->a - cs->b) / (cs->b - cs->a); + double y2 = 2.0 * y; + + double e = 0.0; + + for (j = cs->order; j >= 1; j--) { + double temp = d; + d = y2 * d - dd + cs->c[j]; + e += fabs(y2 * temp) + fabs(dd) + fabs(cs->c[j]); + dd = temp; + } + + { + double temp = d; + d = y * d - dd + 0.5 * cs->c[0]; + e += fabs(y * temp) + fabs(dd) + 0.5 * fabs(cs->c[0]); + } + + result->val = d; + result->err = GSL_DBL_EPSILON * e + fabs(cs->c[cs->order]); + + return GSL_SUCCESS; +} + +/* Chebyshev fit for erfc((t+1)/2), -1 < t < 1 + */ +static double erfc_xlt1_data[20] = { + 1.06073416421769980345174155056, + -0.42582445804381043569204735291, + 0.04955262679620434040357683080, + 0.00449293488768382749558001242, + -0.00129194104658496953494224761, + -0.00001836389292149396270416979, + 0.00002211114704099526291538556, + -5.23337485234257134673693179020e-7, + -2.78184788833537885382530989578e-7, + 1.41158092748813114560316684249e-8, + 2.72571296330561699984539141865e-9, + -2.06343904872070629406401492476e-10, + -2.14273991996785367924201401812e-11, + 2.22990255539358204580285098119e-12, + 1.36250074650698280575807934155e-13, + -1.95144010922293091898995913038e-14, + -6.85627169231704599442806370690e-16, + 1.44506492869699938239521607493e-16, + 2.45935306460536488037576200030e-18, + -9.29599561220523396007359328540e-19 +}; +static cheb_series erfc_xlt1_cs = { + erfc_xlt1_data, + 19, + -1, 1, + 12 +}; + +/* Chebyshev fit for erfc(x) exp(x^2), 1 < x < 5, x = 2t + 3, -1 < t < 1 + */ +static double erfc_x15_data[25] = { + 0.44045832024338111077637466616, + -0.143958836762168335790826895326, + 0.044786499817939267247056666937, + -0.013343124200271211203618353102, + 0.003824682739750469767692372556, + -0.001058699227195126547306482530, + 0.000283859419210073742736310108, + -0.000073906170662206760483959432, + 0.000018725312521489179015872934, + -4.62530981164919445131297264430e-6, + 1.11558657244432857487884006422e-6, + -2.63098662650834130067808832725e-7, + 6.07462122724551777372119408710e-8, + -1.37460865539865444777251011793e-8, + 3.05157051905475145520096717210e-9, + -6.65174789720310713757307724790e-10, + 1.42483346273207784489792999706e-10, + -3.00141127395323902092018744545e-11, + 6.22171792645348091472914001250e-12, + -1.26994639225668496876152836555e-12, + 2.55385883033257575402681845385e-13, + -5.06258237507038698392265499770e-14, + 9.89705409478327321641264227110e-15, + -1.90685978789192181051961024995e-15, + 3.50826648032737849245113757340e-16 +}; +static cheb_series erfc_x15_cs = { + erfc_x15_data, + 24, + -1, 1, + 16 +}; + +/* Chebyshev fit for erfc(x) x exp(x^2), 5 < x < 10, x = (5t + 15)/2, -1 < t < 1 + */ +static double erfc_x510_data[20] = { + 1.11684990123545698684297865808, + 0.003736240359381998520654927536, + -0.000916623948045470238763619870, + 0.000199094325044940833965078819, + -0.000040276384918650072591781859, + 7.76515264697061049477127605790e-6, + -1.44464794206689070402099225301e-6, + 2.61311930343463958393485241947e-7, + -4.61833026634844152345304095560e-8, + 8.00253111512943601598732144340e-9, + -1.36291114862793031395712122089e-9, + 2.28570483090160869607683087722e-10, + -3.78022521563251805044056974560e-11, + 6.17253683874528285729910462130e-12, + -9.96019290955316888445830597430e-13, + 1.58953143706980770269506726000e-13, + -2.51045971047162509999527428316e-14, + 3.92607828989125810013581287560e-15, + -6.07970619384160374392535453420e-16, + 9.12600607264794717315507477670e-17 +}; +static cheb_series erfc_x510_cs = { + erfc_x510_data, + 19, + -1, 1, + 12 +}; + +static double erfc8_sum(double x) +{ + /* estimates erfc(x) valid for 8 < x < 100 */ + /* This is based on index 5725 in Hart et al */ + + static double P[] = { + 2.97886562639399288862, + 7.409740605964741794425, + 6.1602098531096305440906, + 5.019049726784267463450058, + 1.275366644729965952479585264, + 0.5641895835477550741253201704 + }; + static double Q[] = { + 3.3690752069827527677, + 9.608965327192787870698, + 17.08144074746600431571095, + 12.0489519278551290360340491, + 9.396034016235054150430579648, + 2.260528520767326969591866945, + 1.0 + }; + double num = 0.0, den = 0.0; + int i; + + num = P[5]; + for (i = 4; i >= 0; --i) { + num = x * num + P[i]; + } + den = Q[6]; + for (i = 5; i >= 0; --i) { + den = x * den + Q[i]; + } + + return num / den; +} + +inline +static double erfc8(double x) +{ + double e; + e = erfc8_sum(x); + e *= exp(-x * x); + return e; +} + +inline +static double log_erfc8(double x) +{ + double e; + e = erfc8_sum(x); + e = log(e) - x * x; + return e; +} + +int gsl_sf_erfc_e(double x, gsl_sf_result* result) +{ + const double ax = fabs(x); + double e_val, e_err; + + /* CHECK_POINTER(result) */ + + if (ax <= 1.0) { + double t = 2.0 * ax - 1.0; + gsl_sf_result c; + cheb_eval_e(&erfc_xlt1_cs, t, &c); + e_val = c.val; + e_err = c.err; + } + else if (ax <= 5.0) { + double ex2 = exp(-x * x); + double t = 0.5 * (ax - 3.0); + gsl_sf_result c; + cheb_eval_e(&erfc_x15_cs, t, &c); + e_val = ex2 * c.val; + e_err = ex2 * (c.err + 2.0 * fabs(x) * GSL_DBL_EPSILON); + } + else if (ax < 10.0) { + double exterm = exp(-x * x) / ax; + double t = (2.0 * ax - 15.0) / 5.0; + gsl_sf_result c; + cheb_eval_e(&erfc_x510_cs, t, &c); + e_val = exterm * c.val; + e_err = exterm * (c.err + 2.0 * fabs(x) * GSL_DBL_EPSILON + GSL_DBL_EPSILON); + } + else { + e_val = erfc8(ax); + e_err = (x * x + 1.0) * GSL_DBL_EPSILON * fabs(e_val); + } + + if (x < 0.0) { + result->val = 2.0 - e_val; + result->err = e_err; + result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val); + } + else { + result->val = e_val; + result->err = e_err; + result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val); + } + + return GSL_SUCCESS; +} + +inline int gsl_sf_log_erfc_e(double x, gsl_sf_result* result) +{ + /* CHECK_POINTER(result) */ + + if (x * x < 10.0 * GSL_ROOT6_DBL_EPSILON) { + const double y = x / M_SQRTPI; + /* series for -1/2 Log[Erfc[Sqrt[Pi] y]] */ + const double c3 = (4.0 - M_PI) / 3.0; + const double c4 = 2.0 * (1.0 - M_PI / 3.0); + const double c5 = -0.001829764677455021; /* (96.0 - 40.0*M_PI + 3.0*M_PI*M_PI)/30.0 */ + const double c6 = 0.02629651521057465; /* 2.0*(120.0 - 60.0*M_PI + 7.0*M_PI*M_PI)/45.0 */ + const double c7 = -0.01621575378835404; + const double c8 = 0.00125993961762116; + const double c9 = 0.00556964649138; + const double c10 = -0.0045563339802; + const double c11 = 0.0009461589032; + const double c12 = 0.0013200243174; + const double c13 = -0.00142906; + const double c14 = 0.00048204; + double series = c8 + y * (c9 + y * (c10 + y * (c11 + y * (c12 + y * (c13 + c14 * y))))); + series = y * (1.0 + y * (1.0 + y * (c3 + y * (c4 + y * (c5 + y * (c6 + y * (c7 + y * series))))))); + result->val = -2.0 * series; + result->err = 2.0 * GSL_DBL_EPSILON * fabs(result->val); + return GSL_SUCCESS; + } + /* + don't like use of log1p(); added above series stuff for small x instead, should be ok [GJ] + else if (fabs(x) < 1.0) { + gsl_sf_result result_erf; + gsl_sf_erf_e(x, &result_erf); + result->val = log1p(-result_erf.val); + result->err = 2.0 * GSL_DBL_EPSILON * fabs(result->val); + return GSL_SUCCESS; + } + */ + else if (x > 8.0) { + result->val = log_erfc8(x); + result->err = 2.0 * GSL_DBL_EPSILON * fabs(result->val); + return GSL_SUCCESS; + } + else { + gsl_sf_result result_erfc; + gsl_sf_erfc_e(x, &result_erfc); + result->val = log(result_erfc.val); + result->err = fabs(result_erfc.err / result_erfc.val); + result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val); + return GSL_SUCCESS; + } +} + +inline double gsl_sf_log_erfc(double x) +{ + EVAL_RESULT(gsl_sf_log_erfc_e(x, &result)); +} \ No newline at end of file diff --git a/src/lib/interval_tree/interval_tree.hpp b/src/lib/interval_tree/interval_tree.hpp new file mode 100644 index 000000000..364141ba9 --- /dev/null +++ b/src/lib/interval_tree/interval_tree.hpp @@ -0,0 +1,1575 @@ +#pragma once + +#include "interval_tree_fwd.hpp" +#include "interval_types.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace lib_interval_tree +{ +//############################################################################################################ + enum class rb_color + { + fail, + red, + black, + double_black + }; +//############################################################################################################ + using default_interval_value_type = int; +//############################################################################################################ + template + struct interval + { + public: + using value_type = numerical_type; + using interval_kind = interval_kind_; + + /** + * Constructs an interval. low MUST be smaller than high. + */ +#ifndef INTERVAL_TREE_SAFE_INTERVALS +#if __cplusplus >= 201703L + constexpr +#endif + interval(value_type low, value_type high) + : low_{low} + , high_{high} + { + assert(low <= high); + } +#else +#if __cplusplus >= 201703L + constexpr +#endif + interval(value_type low, value_type high) + : low_{std::min(low, high)} + , high_{std::max(low, high)} + { + } +#endif + + /** + * Returns if both intervals equal. + */ + friend bool operator==(interval const& lhs, interval const& other) + { + return lhs.low_ == other.low_ && lhs.high_ == other.high_; + } + + /** + * Returns if both intervals are different. + */ + friend bool operator!=(interval const& lhs, interval const& other) + { + return lhs.low_ != other.low_ || lhs.high_ != other.high_; + } + + /** + * Returns the lower bound of the interval + */ + value_type low() const + { + return low_; + } + + /** + * Returns the upper bound of the interval + */ + value_type high() const + { + return high_; + } + + /** + * Returns whether the intervals overlap. + * For when both intervals are closed. + */ + bool overlaps(value_type l, value_type h) const + { + return low_ <= h && l <= high_; + } + + /** + * Returns whether the intervals overlap, excluding border. + * For when at least one interval is open (l&r). + */ + bool overlaps_exclusive(value_type l, value_type h) const + { + return low_ < h && l < high_; + } + + /** + * Returns whether the intervals overlap + */ + bool overlaps(interval const& other) const + { + return overlaps(other.low_, other.high_); + } + + /** + * Returns whether the intervals overlap, excluding border. + */ + bool overlaps_exclusive(interval const& other) const + { + return overlaps_exclusive(other.low_, other.high_); + } + + /** + * Returns whether the given value is in this. + */ + bool within(value_type value) const + { + return interval_kind::within(low_, high_, value); + } + + /** + * Returns whether the given interval is in this. + */ + bool within(interval const& other) const + { + return low_ <= other.low_ && high_ >= other.high_; + } + + /** + * Calculates the distance between the two intervals. + * Overlapping intervals have 0 distance. + */ + value_type operator-(interval const& other) const + { + if (overlaps(other)) + return 0; + if (high_ < other.low_) + return other.low_ - high_; + else + return low_ - other.high_; + } + + /** + * Returns the size of the interval. + */ + value_type size() const + { + return high_ - low_; + } + + /** + * Creates a new interval from this and other, that contains both intervals and whatever + * is between. + */ + interval join(interval const& other) const + { + return {std::min(low_, other.low_), std::max(high_, other.high_)}; + } + + private: + value_type low_; + value_type high_; + }; +//############################################################################################################ + /** + * Creates a safe interval that puts the lower bound left automatically. + */ + template +#if __cplusplus >= 201703L + constexpr +#endif + interval make_safe_interval(numerical_type lhs, numerical_type rhs) + { + return interval {std::min(lhs, rhs), std::max(lhs, rhs)}; + } +//############################################################################################################ + template > + class node + { + private: + using node_type = node ; + + public: + using interval_type = interval_type_; + using value_type = numerical_type; + + public: + friend lib_interval_tree::interval_tree ; + friend lib_interval_tree::const_interval_tree_iterator >; + friend lib_interval_tree::interval_tree_iterator >; + + public: + node(node* parent, interval_type interval) + : interval_{std::move(interval)} + , max_{interval.high()} + , parent_{parent} + , left_{} + , right_{} + , color_{rb_color::fail} + { + } + + ~node() + { + } + + interval_type interval() const + { + return interval_; + } + + value_type max() const + { + return max_; + } + + bool is_left() const noexcept + { + return this == parent_->left_; + } + + bool is_right() const noexcept + { + return this == parent_->right_; + } + + bool is_root() const noexcept + { + return !parent_; + } + + /** + * Returns the color of the node. + */ + rb_color color() const + { + return color_; + } + + /** + * Returns the parent node up the tree. + */ + node const* parent() const + { + return parent_; + } + + /** + * Returns the left node (readonly). + */ + node const* left() const + { + return left_; + } + + /** + * Returns the right node (readonly). + */ + node const* right() const + { + return right_; + } + + /** + * Returns the height of the node in the tree. Where height = how many parents does it have. + * The root has no parents and is therefor has height 0. + */ + int height() const + { + int counter{0}; + for (auto* p = parent_; p != nullptr; p = p->parent_) + ++counter; + return counter; + } + + /** + * Returns the lower bound of the interval of this node + */ + value_type low() const + { + return interval_.low(); + } + + /** + * Returns the upper bound of the interval of this node + */ + value_type high() const + { + return interval_.high(); + } + +private: + void set_interval(interval_type const& ival) + { + interval_ = ival; + } + + void kill() const noexcept + { + auto* parent = parent_; + if (is_left()) + { + delete parent_->left_; + parent->left_ = nullptr; + } + else + { + delete parent_->right_; + parent->right_ = nullptr; + } + } + + private: + interval_type interval_; + value_type max_; + node* parent_; + node* left_; + node* right_; + rb_color color_; + }; +//############################################################################################################ + template + class basic_interval_tree_iterator : public std::forward_iterator_tag + { + public: + friend interval_tree ; + + using tree_type = interval_tree ; + using value_type = node_type; + + using node_ptr_t = typename std::conditional < + std::is_const ::type>::value, + node_type const*, + node_type* + >::type; + + public: + constexpr basic_interval_tree_iterator(basic_interval_tree_iterator const&) = default; + basic_interval_tree_iterator& operator=(basic_interval_tree_iterator const&) = default; + + bool operator!=(basic_interval_tree_iterator const& other) const + { + return node_ != other.node_; + } + + bool operator==(basic_interval_tree_iterator const& other) const + { + return node_ == other.node_; + } + + /** + * Returns the max property of the node. + */ + typename node_type::interval_type::value_type max() const + { + return node_->max(); + } + + /** + * Returns the color of the node. + */ + rb_color color() const + { + return node_->color(); + } + + typename tree_type::interval_type interval() const + { + return node_->interval(); + } + + virtual ~basic_interval_tree_iterator() = default; + + protected: + basic_interval_tree_iterator(node_ptr_t node, owner_type owner) + : node_{node} + , owner_{owner} + { + } + + protected: + node_ptr_t node_; + owner_type owner_; + }; +//############################################################################################################ + template + class const_interval_tree_iterator + : public basic_interval_tree_iterator const*> + { + public: + using tree_type = interval_tree ; + using iterator_base = basic_interval_tree_iterator ; + using value_type = typename iterator_base::value_type; + using iterator_base::node_; + using iterator_base::owner_; + + friend tree_type; + + public: + const_interval_tree_iterator& operator++() + { + if (!node_) + { + node_ = owner_->root_; + + if (!node_) + return *this; + + while(node_->left_) + node_ = node_->left_; + } + + if (node_->right_) + { + node_ = node_->right_; + + while (node_->left_) + node_ = node_->left_; + } + else + { + auto* parent = node_->parent_; + while (parent != nullptr && node_ == parent->right_) + { + node_ = parent; + parent = parent->parent_; + } + node_ = parent; + } + + return *this; + } + + const_interval_tree_iterator operator++(int) + { + const_interval_tree_iterator cpy = *this; + operator++(); + return cpy; + } + + typename value_type::interval_type operator*() const + { + if (node_) + return node_->interval(); + else + throw std::out_of_range("dereferencing interval_tree_iterator out of bounds"); + } + + /** + * Returns an iterator to the parent of this node. + * will equal std::end(tree) if there is no parent node. + */ + const_interval_tree_iterator parent() const + { + if (node_) + return {node_->parent_, owner_}; + else + throw std::out_of_range("interval_tree_iterator out of bounds"); + } + + /** + * Continues down the left side of this node. + * will equal std::end(tree) if there is no left node. + */ + const_interval_tree_iterator left() const + { + if (node_) + return {node_->left_, owner_}; + else + throw std::out_of_range("interval_tree_iterator out of bounds"); + } + + /** + * Continues down the right side of this node. + * will equal std::end(tree) if there is no right node. + */ + const_interval_tree_iterator right() const + { + if (node_) + return {node_->right_, owner_}; + else + throw std::out_of_range("interval_tree_iterator out of bounds"); + } + + value_type const* operator->() const + { + return node_; + } + + private: + const_interval_tree_iterator(node_type const* node, tree_type const* owner) + : basic_interval_tree_iterator {node, owner} + { + } + }; +//############################################################################################################ + template + class interval_tree_iterator + : public basic_interval_tree_iterator *> + { + public: + using tree_type = interval_tree ; + using iterator_base = basic_interval_tree_iterator ; + using value_type = typename iterator_base::value_type; + using iterator_base::node_; + using iterator_base::owner_; + + friend tree_type; + + public: + interval_tree_iterator& operator++() + { + if (!node_) + { + node_ = owner_->root_; + + if (!node_) + return *this; + + while(node_->left_) + node_ = node_->left_; + } + + if (node_->right_) + { + node_ = node_->right_; + + while (node_->left_) + node_ = node_->left_; + } + else + { + auto* parent = node_->parent_; + while (parent != nullptr && node_ == parent->right_) + { + node_ = parent; + parent = parent->parent_; + } + node_ = parent; + } + + return *this; + } + + interval_tree_iterator operator++(int) + { + interval_tree_iterator cpy = *this; + operator++(); + return cpy; + } + + /** + * Returns an iterator to the parent of this node. + * will equal std::end(tree) if there is no parent node. + */ + interval_tree_iterator parent() + { + if (node_) + return {node_->parent_, owner_}; + else + throw std::out_of_range("interval_tree_iterator out of bounds"); + } + + /** + * Continues down the left side of this node. + * will equal std::end(tree) if there is no left node. + */ + interval_tree_iterator left() + { + if (node_) + return {node_->left_, owner_}; + else + throw std::out_of_range("interval_tree_iterator out of bounds"); + } + + /** + * Continues down the right side of this node. + * will equal std::end(tree) if there is no right node. + */ + interval_tree_iterator right() + { + if (node_) + return {node_->right_, owner_}; + else + throw std::out_of_range("interval_tree_iterator out of bounds"); + } + + typename value_type::interval_type operator*() const + { + if (node_) + return node_->interval(); + else + throw std::out_of_range("interval_tree_iterator out of bounds"); + } + + value_type* operator->() + { + return node_; + } + + private: + interval_tree_iterator(node_type* node, tree_type* owner) + : basic_interval_tree_iterator {node, owner} + { + } + }; +//############################################################################################################ + template > + class interval_tree + { + public: + using interval_type = IntervalT; + using value_type = typename interval_type::value_type; + using node_type = node ; + using iterator = interval_tree_iterator ; + using const_iterator = const_interval_tree_iterator ; + using size_type = long long; + using this_type = interval_tree; + + public: + friend const_interval_tree_iterator ; + friend interval_tree_iterator ; + + interval_tree() + : root_{nullptr} + , size_{0} + { + } + + ~interval_tree() + { + clear(); + } + + interval_tree(interval_tree const& other) + : root_{nullptr} + , size_{0} + { + operator=(other); + } + + public: + interval_tree& operator=(interval_tree const& other) + { + if (!empty()) + clear(); + + if (other.root_ != nullptr) + root_ = copyTreeImpl(other.root_, nullptr); + + size_ = other.size_; + + return *this; + } + + /** + * Removes all from this tree. + */ + void clear() + { + for (auto i = std::begin(*this); i != std::end(*this);) + i = erase(i); + } + + /** + * Returns the root node from this tree. + */ + iterator root() + { + return {root_, this}; + } + + /** + * Returns the root node from this tree. + */ + const_iterator root() const + { + return {root_, this}; + } + + /** + * Inserts an interval into the tree. + */ + template + iterator insert(IntervalType&& ival) + { + node_type* z = new node_type(nullptr, std::forward (ival)); + node_type* y = nullptr; + node_type* x = root_; + while (x) + { + y = x; + if (z->interval_.low() < x->interval_.low()) + x = x->left_; + else + x = x->right_; + } + z->parent_ = y; + if (!y) + root_ = z; + else if (z->interval_.low() < y->interval_.low()) + y->left_ = z; + else + y->right_ = z; + z->color_ = rb_color::red; + + insert_fixup(z); + recalculate_max(z); + ++size_; + return {z, this}; + } + + /** + * Inserts an interval into the tree if no other interval overlaps it. + * Otherwise merge the interval with the being overlapped. + * + * @param ival The interval + * @param exclusive Exclude borders. + */ + iterator insert_overlap(interval_type const& ival, bool exclusive = false) + { + auto iter = overlap_find(ival, exclusive); + if (iter == end()) + return insert(ival); + else + { + auto merged = iter->interval().join(ival); + erase(iter); + return insert_overlap(merged); + } + } + + /** + * Erases the element pointed to be iter. + */ + iterator erase(iterator iter) + { + if (!iter.node_) + throw std::out_of_range("cannot erase end iterator"); + + auto next = iter; + ++next; + + node_type* y; + if (!iter.node_->left_ || !iter.node_->right_) + y = iter.node_; + else + y = successor(iter.node_); + + node_type* x; + if (y->left_) + x = y->left_; + else + x = y->right_; + + if (x) + x->parent_ = y->parent_; + + auto* x_parent = y->parent_; + + if (!y->parent_) + root_ = x; + else if (y->is_left()) + y->parent_->left_ = x; + else + y->parent_->right_ = x; + + if (y != iter.node_) + { + iter.node_->interval_ = y->interval_; + iter.node_->max_ = y->max_; + recalculate_max(iter.node_); + } + + if (x && x->color_ == rb_color::red) + { + if (x_parent) + erase_fixup(x, x_parent, y->is_left()); + else + x->color_ = rb_color::black; + } + + delete y; + + --size_; + return next; + } + + /** + * Returns the size of the object. + */ + size_type size() const + { + return size_; + } + + /** + * Finds the first exact match. + * + * @param ival The interval to find an exact match for within the tree. + * @param compare A comparison function to use. + */ + template + iterator find(interval_type const& ival, CompareFunctionT const& compare) + { + if (root_ == nullptr) + return end(); + return iterator{find_i(root_, ival, compare), this}; + } + + /** + * Finds the first exact match. + * + * @param ival The interval to find an exact match for within the tree. + * @param compare A comparison function to use. + */ + template + const_iterator find(interval_type const& ival, CompareFunctionT const& compare) const + { + if (root_ == nullptr) + return end(); + return const_iterator{find_i(root_, ival, compare), this}; + } + + /** + * Finds the first exact match. + * + * @param ival The interval to find an exact match for within the tree. + */ + iterator find(interval_type const& ival) + { + return find(ival, [](auto const& lhs, auto const& rhs){return lhs == rhs;}); + } + /** + * Finds the first exact match. + * + * @param ival The interval to find an exact match for within the tree. + */ + const_iterator find(interval_type const& ival) const + { + return find(ival, [](auto const& lhs, auto const& rhs){return lhs == rhs;}); + } + + /** + * Finds all exact matches and returns the amount of intervals found. + */ + template + void find_all(interval_type const& ival, FunctionT const& on_find, CompareFunctionT const& compare) + { + if (root_ == nullptr) + return; + find_all_i(this, root_, ival, on_find, compare); + } + template + void find_all(interval_type const& ival, FunctionT const& on_find, CompareFunctionT const& compare) const + { + if (root_ == nullptr) + return; + find_all_i(this, root_, ival, on_find, compare); + } + + template + void find_all(interval_type const& ival, FunctionT const& on_find) + { + find_all(ival, on_find, [](auto const& lhs, auto const& rhs){return lhs == rhs;}); + } + template + void find_all(interval_type const& ival, FunctionT const& on_find) const + { + find_all(ival, on_find, [](auto const& lhs, auto const& rhs){return lhs == rhs;}); + } + + /** + * Finds the next exact match EXCLUDING from. + * + * @param from The iterator to search from EXCLUSIVE! + * @param ival The interval to find an exact match for within the tree. + * @param compare A comparison function to use. + */ + template + iterator find_next_in_subtree(iterator from, interval_type const& ival, CompareFunctionT const& compare) + { + if (root_ == nullptr) + return end(); + return iterator{find_i_ex(from.node_, ival, compare), this}; + } + template + const_iterator find_next_in_subtree(iterator from, interval_type const& ival, CompareFunctionT const& compare) const + { + if (root_ == nullptr) + return end(); + return iterator{find_i_ex(from.node_, ival, compare), this}; + } + + /** + * Finds the next exact match EXCLUDING from. + * + * @param from The iterator to search from, EXCLUSIVE! + * @param ival The interval to find an exact match for within the tree. + * @param compare A comparison function to use. + */ + iterator find_next_in_subtree(iterator from, interval_type const& ival) + { + return find_next_in_subtree(from, ival, [](auto const& lhs, auto const& rhs){return lhs == rhs;}); + } + const_iterator find_next_in_subtree(iterator from, interval_type const& ival) const + { + return find_next_in_subtree(from, ival, [](auto const& lhs, auto const& rhs){return lhs == rhs;}); + } + + /** + * Finds the first interval that overlaps with ival. + * + * @param ival The interval to find an overlap for within the tree. + * @param exclusive Exclude edges? + */ + iterator overlap_find(interval_type const& ival, bool exclusive = false) + { + if (root_ == nullptr) + return end(); + if (exclusive) + return iterator{overlap_find_i(root_, ival), this}; + else + return iterator{overlap_find_i(root_, ival), this}; + } + const_iterator overlap_find(interval_type const& ival, bool exclusive = false) const + { + if (root_ == nullptr) + return end(); + if (exclusive) + return const_iterator{overlap_find_i(root_, ival), this}; + else + return const_iterator{overlap_find_i(root_, ival), this}; + } + + /** + * Finds all intervals that overlaps with ival. + * + * @param ival The interval to find an overlap for within the tree. + * @param exclusive Exclude edges? + */ + template + void overlap_find_all(interval_type const& ival, FunctionT& on_find, bool exclusive = false) + { + if (root_ == nullptr) + return; + if (exclusive) + overlap_find_all_i(this, root_, ival, on_find); + else + overlap_find_all_i(this, root_, ival, on_find); + } + template + void overlap_find_all(interval_type const& ival, FunctionT& on_find, bool exclusive = false) const + { + if (root_ == nullptr) + return; + if (exclusive) + overlap_find_all_i(this, root_, ival, on_find); + else + overlap_find_all_i(this, root_, ival, on_find); + } + + /** + * Finds the next interval that overlaps with ival + * + * @param from The iterator to start from, EXCLUSIVE! + * @param ival The interval to find an overlap for within the tree. + * @param exclusive Exclude edges? + */ + iterator overlap_find_next_in_subtree(iterator from, interval_type const& ival, bool exclusive = false) + { + if (root_ == nullptr) + return end(); + return iterator{overlap_find_i_ex(from.node_, ival, exclusive), this}; + } + const_iterator overlap_find_next_in_subtree(const_iterator from, interval_type const& ival, bool exclusive = false) const + { + if (root_ == nullptr) + return end(); + return const_iterator {overlap_find_i_ex(from.node_, ival, exclusive), this}; + } + + /** + * Deoverlaps the tree but returns it as a copy. + */ + interval_tree deoverlap_copy() + { + interval_tree fresh; + for (auto i = begin(), e = end(); i != e; ++i) + fresh.insert_overlap(*i); + + return fresh; + } + + /** + * Merges all overlapping intervals by erasing overlapping intervals and reinserting the merged interval. + */ + interval_tree& deoverlap() + { + *this = deoverlap_copy(); + return *this; + } + + /** + * Only works with deoverlapped trees. + * Creates an interval tree that contains all gaps between the intervals as intervals. + */ + interval_tree punch() const + { + if (empty()) + return {}; + auto min = std::begin(*this)->interval().low(); + auto max = root_->max_; + return punch({min, max}); + } + + /** + * Only works with deoverlapped trees. + * Removes all intervals from the given interval and produces a tree that contains the remaining intervals. + * This is basically the other punch overload with ival = [tree_lowest, tree_highest] + */ + interval_tree punch(interval_type const& ival) const + { + if (empty()) + return {}; + + interval_tree result; + auto i = std::begin(*this); + if (ival.low() < i->interval().low()) + result.insert({ival.low(), i->interval().low()}); + + for (auto e = end(); i != e; ++i) + { + auto next = i; ++next; + if (next != e) + result.insert({i->interval().high(), next->interval().low()}); + else + break; + } + + if (i != end() && i->interval().high() < ival.high()) + result.insert({i->interval().high(), ival.high()}); + + return result; + } + + iterator begin() + { + if (!root_) + return {nullptr, this}; + + auto* iter = root_; + + while (iter->left_) + iter = iter->left_; + + return{iter, this}; + } + iterator end() + { + return {nullptr, this}; + } + + const_iterator cbegin() const + { + if (!root_) + return {nullptr, this}; + + auto* iter = root_; + + while (iter->left_) + iter = iter->left_; + + return const_iterator{iter, this}; + } + const_iterator cend() const + { + return const_iterator{nullptr, this}; + } + const_iterator begin() const + { + return cbegin(); + } + const_iterator end() const + { + return cend(); + } + + /** + * Returns wether or not the tree is empty + */ + bool empty() const noexcept + { + return root_ == nullptr; + } + + private: + node_type* copyTreeImpl(node_type* root, node_type* parent) + { + if (root) + { + auto* cpy = new node_type(parent, root->interval()); + cpy->color_ = root->color_; + cpy->max_ = root->max_; + cpy->left_ = copyTreeImpl(root->left_, cpy); + cpy->right_ = copyTreeImpl(root->right_, cpy); + return cpy; + } + return nullptr; + }; + + template + static bool find_all_i + ( + typename std::conditional::value, ThisType, ThisType const>::type* self, + node_type* ptr, + interval_type const& ival, + FunctionT const& on_find, + ComparatorFunctionT const& compare + ) + { + if (compare(ptr->interval(), ival)) + { + if (!on_find(IteratorT{ptr, self})) + return false; + } + if (ptr->left_ && ival.high() <= ptr->left_->max()) + { + // no right? can only continue left + if (!ptr->right_ || ival.low() > ptr->right_->max()) + return find_all_i(self, ptr->left_, ival, on_find, compare); + + if (!find_all_i(self, ptr->left_, ival, on_find, compare)) + return false; + } + if (ptr->right_ && ival.high() <= ptr->right_->max()) + { + if (!ptr->left_ || ival.low() > ptr->left_->max()) + return find_all_i(self, ptr->right_, ival, on_find, compare); + + if (!find_all_i(self, ptr->right_, ival, on_find, compare)) + return false; + } + return true; + } + + template + node_type* find_i(node_type* ptr, interval_type const& ival, ComparatorFunctionT const& compare) const + { + if (compare(ptr->interval(), ival)) + return ptr; + else + return find_i_ex(ptr, ival, compare); + } + + // excludes ptr + template + node_type* find_i_ex(node_type* ptr, interval_type const& ival, ComparatorFunctionT const& compare) const + { + if (ptr->left_ && ival.high() <= ptr->left_->max()) + { + // no right? can only continue left + if (!ptr->right_ || ival.low() > ptr->right_->max()) + return find_i(ptr->left_, ival, compare); + + auto* res = find_i(ptr->left_, ival, compare); + if (res != nullptr) + return res; + } + if (ptr->right_ && ival.high() <= ptr->right_->max()) + { + if (!ptr->left_ || ival.low() > ptr->left_->max()) + return find_i(ptr->right_, ival, compare); + + auto* res = find_i(ptr->right_, ival, compare); + if (res != nullptr) + return res; + } + return nullptr; + } + + template + node_type* overlap_find_i(node_type* ptr, interval_type const& ival) const + { +#if __cplusplus >= 201703L + if constexpr (Exclusive) +#else + if (Exclusive) +#endif + { + if (ptr->interval().overlaps_exclusive(ival)) + return ptr; + } + else + { + if (ptr->interval().overlaps(ival)) + return ptr; + } + + return overlap_find_i_ex(ptr, ival); + } + + template + static bool overlap_find_all_i + ( + typename std::conditional::value, ThisType, ThisType const>::type* self, + node_type* ptr, + interval_type const& ival, + FunctionT& on_find + ) + { +#if __cplusplus >= 201703L + if constexpr (Exclusive) +#else + if (Exclusive) +#endif + { + if (ptr->interval().overlaps_exclusive(ival)) + { + if (!on_find(IteratorT{ptr, self})) + { + return false; + } + } + } + else + { + if (ptr->interval().overlaps(ival)) + { + if (!on_find(IteratorT{ptr, self})) + { + return false; + } + } + } + if (ptr->left_ && ptr->left_->max() >= ival.low()) + { + // no right? can only continue left + // or interval low is bigger than max of right branch. + if (!ptr->right_ || ival.low() > ptr->right_->max()) + return overlap_find_all_i(self, ptr->left_, ival, on_find); + + if (!overlap_find_all_i(self, ptr->left_, ival, on_find)) + return false; + } + if (ptr->right_ && ptr->right_->max() >= ival.low()) + { + if (!ptr->left_ || ival.low() > ptr->right_->max()) + return overlap_find_all_i(self, ptr->right_, ival, on_find); + + if (!overlap_find_all_i(self, ptr->right_, ival, on_find)) + return false; + } + return true; + } + + // excludes ptr + template + node_type* overlap_find_i_ex(node_type* ptr, interval_type const& ival) const + { + if (ptr->left_ && ptr->left_->max() >= ival.low()) + { + // no right? can only continue left + // or upper bounds higher than what is contained right? continue left. + if (!ptr->right_ || ival.low() > ptr->right_->max()) + return overlap_find_i(ptr->left_, ival); + + auto* res = overlap_find_i(ptr->left_, ival); + if (res != nullptr) + return res; + } + if (ptr->right_ && ptr->right_->max() >= ival.low()) + { + if (!ptr->left_ || ival.low() > ptr->left_->max()) + return overlap_find_i(ptr->right_, ival); + + auto* res = overlap_find_i(ptr->right_, ival); + if (res != nullptr) + return res; + } + return nullptr; + } + + node_type* successor(node_type* node) + { + if (node->right_) + return minimum(node->right_); + auto* y = node->parent_; + while (y && node == y->right_) + { + node = y; + y = y->parent_; + } + return y; + } + + bool is_descendant(iterator par, iterator desc) + { + auto p = desc->parent_; + for (; p && p != par.node_; p = p->parent_) {} + return p != nullptr; + } + + /** + * Set v inplace of u. Does not delete u. + * Creates orphaned nodes. A transplant call must be succeeded by delete calls. + */ + void transplant(node_type* u, node_type* v) + { + if (u->is_root()) + root_ = v; + else if (u->is_left()) + u->parent_->left_ = v; + else + u->parent_->right_ = v; + if (v) + v->parent_ = u->parent_; + } + + /** + * Get leftest of x. + */ + node_type* minimum(node_type* x) const + { + while (x->left_) + x = x->left_; + return x; + } + + void left_rotate(node_type* x) + { + auto* y = x->right_; + x->right_ = y->left_; + if (y->left_) + y->left_->parent_ = x; + + y->parent_ = x->parent_; + if (!x->parent_) + root_ = y; + else if (x->is_left()) + x->parent_->left_ = y; + else + x->parent_->right_ = y; + + y->left_ = x; + x->parent_ = y; + + // max fixup + if (x->left_ && x->right_) + x->max_ = std::max(x->interval_.high(), std::max(x->left_->max_, x->right_->max_)); + else if (x->left_) + x->max_ = std::max(x->interval_.high(), x->left_->max_); + else if (x->right_) + x->max_ = std::max(x->interval_.high(), x->right_->max_); + else + x->max_ = x->interval_.high(); + + if (y->right_) + y->max_ = std::max(y->interval_.high(), std::max(y->right_->max_, x->max_)); + else + y->max_ = std::max(y->interval_.high(), x->max_); + } + + void right_rotate(node_type* y) + { + auto* x = y->left_; + y->left_ = x->right_; + + if (x->right_) + x->right_->parent_ = y; + + x->parent_= y->parent_; + if (!y->parent_) + root_ = x; + else if (y->is_left()) + y->parent_->left_ = x; + else + y->parent_->right_ = x; + + x->right_ = y; + y->parent_ = x; + + // max fixup + if (y->left_ && y->right_) + y->max_ = std::max(y->interval_.high(), std::max(y->left_->max_, y->right_->max_)); + else if (y->left_) + y->max_ = std::max(y->interval_.high(), y->left_->max_); + else if (y->right_) + y->max_ = std::max(y->interval_.high(), y->right_->max_); + else + y->max_ = y->interval_.high(); + + if (x->left_) + x->max_ = std::max(x->interval_.high(), std::max(x->left_->max_, y->max_)); + else + x->max_ = std::max(x->interval_.high(), y->max_); + } + + void recalculate_max(node_type* reacalculation_root) + { + auto* p = reacalculation_root; + while (p && p->max_ <= reacalculation_root->max_) + { + if (p->left_ && p->left_->max_ > p->max_) + p->max_ = p->left_->max_; + if (p->right_ && p->right_->max_ > p->max_) + p->max_ = p->right_->max_; + p = p->parent_; + } + } + + void insert_fixup(node_type* z) + { + while (z->parent_ && z->parent_->color_ == rb_color::red) + { + if (!z->parent_->parent_) + break; + if (z->parent_ == z->parent_->parent_->left_) + { + node_type* y = z->parent_->parent_->right_; + if (y && y->color_ == rb_color::red) + { + z->parent_->color_ = rb_color::black; + y->color_ = rb_color::black; + z->parent_->parent_->color_ = rb_color::red; + z = z->parent_->parent_; + } + else + { + if (z == z->parent_->right_) + { + z = z->parent_; + left_rotate(z); + } + z->parent_->color_ = rb_color::black; + z->parent_->parent_->color_ = rb_color::red; + right_rotate(z->parent_->parent_); + } + } + else + { + node_type* y = z->parent_->parent_->left_; + if (y && y->color_ == rb_color::red) + { + z->parent_->color_ = rb_color::black; + y->color_ = rb_color::black; + z->parent_->parent_->color_ = rb_color::red; + z = z->parent_->parent_; + } + else + { + if (z->is_left()) + { + z = z->parent_; + right_rotate(z); + } + z->parent_->color_ = rb_color::black; + z->parent_->parent_->color_ = rb_color::red; + left_rotate(z->parent_->parent_); + } + } + } + root_->color_ = rb_color::black; + } + + void erase_fixup(node_type* x, node_type* x_parent, bool y_is_left) + { + while (x != root_ && x->color_ == rb_color::black) + { + node_type* w; + if (y_is_left) + { + w = x_parent->right_; + if (w->color_ == rb_color::red) + { + w->color_ = rb_color::black; + x_parent->color_ = rb_color::red; + left_rotate(x_parent); + w = x_parent->right_; + } + + if (w->left_->color_ == rb_color::black && w->right_->color_ == rb_color::black) + { + w->color_ = rb_color::red; + x = x_parent; + x_parent = x->parent_; + y_is_left = (x == x_parent->left_); + } + else + { + if (w->right_->color_ == rb_color::black) + { + w->left_->color_ = rb_color::black; + w->color_ = rb_color::red; + right_rotate(w); + w = x_parent->right_; + } + + w->color_ = x_parent->color_; + x_parent->color_ = rb_color::black; + if (w->right_) + w->right_->color_ = rb_color::black; + + left_rotate(x_parent); + x = root_; + x_parent = nullptr; + } + } + else + { + w = x_parent->left_; + if (w->color_ == rb_color::red) + { + w->color_ = rb_color::black; + x_parent->color_ = rb_color::red; + right_rotate(x_parent); + w = x_parent->left_; + } + + if (w->right_->color_ == rb_color::black && w->left_->color_ == rb_color::black) + { + w->color_ = rb_color::red; + x = x_parent; + x_parent = x->parent_; + y_is_left = (x == x_parent->left_); + } + else + { + if (w->left_->color_ == rb_color::black) + { + w->right_->color_ = rb_color::black; + w->color_ = rb_color::red; + left_rotate(w); + w = x_parent->left_; + } + + w->color_ = x_parent->color_; + x_parent->color_ = rb_color::black; + if (w->left_) + w->left_->color_ = rb_color::black; + + right_rotate(x_parent); + x = root_; + x_parent = nullptr; + } + } + } + + x->color_ = rb_color::black; + } + + private: + node_type* root_; + size_type size_; + }; +//############################################################################################################ + template + using interval_tree_t = interval_tree >; +//############################################################################################################ +} diff --git a/src/lib/interval_tree/interval_tree_fwd.hpp b/src/lib/interval_tree/interval_tree_fwd.hpp new file mode 100644 index 000000000..c9a5a0e33 --- /dev/null +++ b/src/lib/interval_tree/interval_tree_fwd.hpp @@ -0,0 +1,22 @@ +#pragma once + +namespace lib_interval_tree +{ + template + struct interval; + + template + class interval_tree; + + template + class node; + + template + class basic_interval_tree_iterator; + + template + class const_interval_tree_iterator; + + template + class interval_tree_iterator; +} diff --git a/src/lib/interval_tree/interval_types.hpp b/src/lib/interval_tree/interval_types.hpp new file mode 100644 index 000000000..eb37a93c4 --- /dev/null +++ b/src/lib/interval_tree/interval_types.hpp @@ -0,0 +1,41 @@ +#pragma once + +namespace lib_interval_tree +{ + // (] + struct left_open + { + template + static inline bool within(numerical_type b, numerical_type e, numerical_type p) + { + return (b < p) && (p <= e); + } + }; + // [) + struct right_open + { + template + static inline bool within(numerical_type b, numerical_type e, numerical_type p) + { + return (b <= p) && (p < e); + } + }; + // [] + struct closed + { + template + static inline bool within(numerical_type b, numerical_type e, numerical_type p) + { + return (b <= p) && (p <= e); + } + }; + // () + struct open + { + template + static inline bool within(numerical_type b, numerical_type e, numerical_type p) + { + return (b < p) && (p < e); + } + }; +} diff --git a/src/lib/ips4o/block_permutation.hpp b/src/lib/ips4o/block_permutation.hpp index 3773130be..f1378b378 100644 --- a/src/lib/ips4o/block_permutation.hpp +++ b/src/lib/ips4o/block_permutation.hpp @@ -33,6 +33,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +// Modified by B. Buchfink + #pragma once #include @@ -49,8 +51,8 @@ namespace detail { * i.e., the last bucket that has more than one full block. */ template -int Sorter::computeOverflowBucket() { - int bucket = num_buckets_ - 1; +typename Cfg::bucket_type Sorter::computeOverflowBucket() { + typename Cfg::bucket_type bucket = num_buckets_ - 1; while (bucket >= 0 && (bucket_start_[bucket + 1] - bucket_start_[bucket]) <= Cfg::kBlockSize) --bucket; @@ -62,7 +64,7 @@ int Sorter::computeOverflowBucket() { */ template template -int Sorter::classifyAndReadBlock(const int read_bucket) { +typename Cfg::bucket_type Sorter::classifyAndReadBlock(const typename Cfg::bucket_type read_bucket) { auto& bp = bucket_pointers_[read_bucket]; diff_t write, read; @@ -86,10 +88,10 @@ int Sorter::classifyAndReadBlock(const int read_bucket) { */ template template -int Sorter::swapBlock(const diff_t max_off, const int dest_bucket, +typename Cfg::bucket_type Sorter::swapBlock(const diff_t max_off, const typename Cfg::bucket_type dest_bucket, const bool current_swap) { diff_t write, read; - int new_dest_bucket; + typename Cfg::bucket_type new_dest_bucket; auto& bp = bucket_pointers_[dest_bucket]; do { std::tie(write, read) = bp.template incWrite(); @@ -126,13 +128,13 @@ template void Sorter::permuteBlocks() { const auto num_buckets = num_buckets_; // Distribute starting points of threads - int read_bucket = (my_id_ * num_buckets / num_threads_) % num_buckets; + typename Cfg::bucket_type read_bucket = (my_id_ * num_buckets / num_threads_) % num_buckets; // Not allowed to write to this offset, to avoid overflow const diff_t max_off = Cfg::alignToNextBlock(end_ - begin_ + 1) - Cfg::kBlockSize; // Go through all buckets - for (int count = num_buckets; count; --count) { - int dest_bucket; + for (typename Cfg::bucket_type count = num_buckets; count; --count) { + typename Cfg::bucket_type dest_bucket; // Try to read a block ... while ((dest_bucket = classifyAndReadBlock(read_bucket)) != -1) { bool current_swap = 0; diff --git a/src/lib/ips4o/buffers.hpp b/src/lib/ips4o/buffers.hpp index 5fce8cca3..011a0505d 100644 --- a/src/lib/ips4o/buffers.hpp +++ b/src/lib/ips4o/buffers.hpp @@ -161,28 +161,28 @@ class Sorter::Buffers { /** * Checks if buffer is full. */ - bool isFull(const int i) const { + bool isFull(const int64_t i) const { return buffer_[i].ptr == buffer_[i].end; } /** * Pointer to buffer data. */ - value_type* data(const int i) { + value_type* data(const int64_t i) { return static_cast(static_cast(storage_)) + i * Cfg::kBlockSize; } /** * Number of elements in buffer. */ - diff_t size(const int i) const { + diff_t size(const int64_t i) const { return Cfg::kBlockSize - (buffer_[i].end - buffer_[i].ptr); } /** * Resets buffer. */ - void reset(const int i) { + void reset(const int64_t i) { if (Block::kDestruct) for (auto p = data(i), end = p + size(i); p < end; ++p) p->~value_type(); @@ -192,7 +192,7 @@ class Sorter::Buffers { /** * Pushes new element to buffer. */ - void push(const int i, value_type&& value) { + void push(const int64_t i, value_type&& value) { if (Block::kInitializedStorage) { *buffer_[i].ptr++ = std::move(value); } else { @@ -204,7 +204,7 @@ class Sorter::Buffers { /** * Flushes buffer to input. */ - void writeTo(const int i, typename Cfg::iterator dest) { + void writeTo(const int64_t i, typename Cfg::iterator dest) { resetBuffer(i); auto ptr = buffer_[i].ptr; std::move(ptr, ptr + Cfg::kBlockSize, std::move(dest)); @@ -220,7 +220,7 @@ class Sorter::Buffers { const value_type* end; }; - void resetBuffer(const int i) { + void resetBuffer(const typename Cfg::difference_type i) { buffer_[i].ptr = static_cast(static_cast(storage_)) + i * Cfg::kBlockSize; } diff --git a/src/lib/ips4o/classifier.hpp b/src/lib/ips4o/classifier.hpp index bfb621254..635cb2fe1 100644 --- a/src/lib/ips4o/classifier.hpp +++ b/src/lib/ips4o/classifier.hpp @@ -33,6 +33,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +// Modified by B. Buchfink + #pragma once #include @@ -85,7 +87,7 @@ class Sorter::Classifier { */ void build(int log_buckets) { log_buckets_ = log_buckets; - num_buckets_ = 1 << log_buckets; + num_buckets_ = (bucket_type)1 << log_buckets; const auto num_splitters = (1 << log_buckets) - 1; IPS4O_ASSUME_NOT(getSortedSplitters() + num_splitters == nullptr); new (getSortedSplitters() + num_splitters) value_type(getSortedSplitters()[num_splitters - 1]); @@ -198,7 +200,7 @@ class Sorter::Classifier { void cleanup() { auto p = data() + 1; auto q = getSortedSplitters(); - for (int i = num_buckets_ - 1; i; --i) { + for (bucket_type i = num_buckets_ - 1; i; --i) { p++->~value_type(); q++->~value_type(); } diff --git a/src/lib/ips4o/cleanup_margins.hpp b/src/lib/ips4o/cleanup_margins.hpp index 39cea5d44..b9e386c72 100644 --- a/src/lib/ips4o/cleanup_margins.hpp +++ b/src/lib/ips4o/cleanup_margins.hpp @@ -33,6 +33,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +// Modified by B. Buchfink + #pragma once #include @@ -50,7 +52,7 @@ namespace detail { * Saves margins at thread boundaries. */ template -std::pair Sorter::saveMargins(int last_bucket) { +std::pair Sorter::saveMargins(typename Cfg::bucket_type last_bucket) { // Find last bucket boundary in this thread's area diff_t tail = bucket_start_[last_bucket]; const diff_t end = Cfg::alignToNextBlock(tail); @@ -85,13 +87,13 @@ std::pair Sorter::saveMargins(int last_ * Fills margins from buffers. */ template -void Sorter::writeMargins(const int first_bucket, const int last_bucket, - const int overflow_bucket, const int swap_bucket, +void Sorter::writeMargins(const typename Cfg::bucket_type first_bucket, const typename Cfg::bucket_type last_bucket, + const typename Cfg::bucket_type overflow_bucket, const typename Cfg::bucket_type swap_bucket, const diff_t in_swap_buffer) { const bool is_last_level = end_ - begin_ <= Cfg::kSingleLevelThreshold; const auto comp = classifier_->getComparator(); - for (int i = first_bucket; i < last_bucket; ++i) { + for (typename Cfg::bucket_type i = first_bucket; i < last_bucket; ++i) { // Get bucket information const ptrdiff_t bstart = bucket_start_[i]; const ptrdiff_t bend = bucket_start_[i + 1]; diff --git a/src/lib/ips4o/empty_block_movement.hpp b/src/lib/ips4o/empty_block_movement.hpp index 2c3180fab..082d5edd0 100644 --- a/src/lib/ips4o/empty_block_movement.hpp +++ b/src/lib/ips4o/empty_block_movement.hpp @@ -33,6 +33,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +// Modified by B. Buchfink + #pragma once #include @@ -51,11 +53,11 @@ template void Sorter::moveEmptyBlocks(const diff_t my_begin, const diff_t my_end, const diff_t my_first_empty_block) { // Find range of buckets that start in this stripe - const int bucket_range_start = [&](int i) { + const typename Cfg::bucket_type bucket_range_start = [&](typename Cfg::bucket_type i) { while (Cfg::alignToNextBlock(bucket_start_[i]) < my_begin) ++i; return i; }(0); - const int bucket_range_end = [&](int i) { + const typename Cfg::bucket_type bucket_range_end = [&](typename Cfg::bucket_type i) { const auto num_buckets = num_buckets_; if (my_id_ == num_threads_ - 1) return num_buckets; while (i < num_buckets && Cfg::alignToNextBlock(bucket_start_[i]) < my_end) ++i; @@ -87,7 +89,7 @@ void Sorter::moveEmptyBlocks(const diff_t my_begin, const diff_t my_end, const bool last_bucket_is_overlapping = bucket_end > my_end; // Case 1) - for (int b = bucket_range_start; b < bucket_range_end - last_bucket_is_overlapping; ++b) { + for (typename Cfg::bucket_type b = bucket_range_start; b < bucket_range_end - last_bucket_is_overlapping; ++b) { const auto start = Cfg::alignToNextBlock(bucket_start_[b]); const auto stop = Cfg::alignToNextBlock(bucket_start_[b + 1]); auto read = stop; @@ -103,7 +105,7 @@ void Sorter::moveEmptyBlocks(const diff_t my_begin, const diff_t my_end, // Cases 2) and 3) if (last_bucket_is_overlapping) { - const int overlapping_bucket = bucket_range_end - 1; + const typename Cfg::bucket_type overlapping_bucket = bucket_range_end - 1; const auto bucket_start = Cfg::alignToNextBlock(bucket_start_[overlapping_bucket]); // If it is a very large bucket, other threads will also move blocks around in it (case 3) diff --git a/src/lib/ips4o/ips4o_fwd.hpp b/src/lib/ips4o/ips4o_fwd.hpp index caa176453..9661bc927 100644 --- a/src/lib/ips4o/ips4o_fwd.hpp +++ b/src/lib/ips4o/ips4o_fwd.hpp @@ -94,13 +94,13 @@ class Sorter { iterator begin_; iterator end_; - int num_buckets_; + typename Cfg::bucket_type num_buckets_; int my_id_; int num_threads_; static inline int computeLogBuckets(diff_t n); - std::pair buildClassifier(iterator begin, iterator end, Classifier& classifier); + std::pair buildClassifier(iterator begin, iterator end, Classifier& classifier); template FLATTEN diff_t classifyLocally(iterator my_begin, iterator my_end); @@ -111,24 +111,24 @@ class Sorter { void moveEmptyBlocks(diff_t my_begin, diff_t my_end, diff_t my_first_empty_block); - inline int computeOverflowBucket(); + inline typename Cfg::bucket_type computeOverflowBucket(); template - inline int classifyAndReadBlock(int read_bucket); + inline typename Cfg::bucket_type classifyAndReadBlock(typename Cfg::bucket_type read_bucket); template - inline int swapBlock(diff_t max_off, int dest_bucket, bool current_swap); + inline typename Cfg::bucket_type swapBlock(diff_t max_off, typename Cfg::bucket_type dest_bucket, bool current_swap); template void permuteBlocks(); - inline std::pair saveMargins(int last_bucket); + inline std::pair saveMargins(typename Cfg::bucket_type last_bucket); - void writeMargins(int first_bucket, int last_bucket, int overflow_bucket, - int swap_bucket, diff_t in_swap_buffer); + void writeMargins(typename Cfg::bucket_type first_bucket, typename Cfg::bucket_type last_bucket, typename Cfg::bucket_type overflow_bucket, + typename Cfg::bucket_type swap_bucket, diff_t in_swap_buffer); template - std::pair partition(iterator begin, iterator end, std::atomic_ptrdiff_t* bucket_start, + std::pair partition(iterator begin, iterator end, std::atomic_ptrdiff_t* bucket_start, SharedData* shared, int my_id, int num_threads); inline void processSmallTasks(iterator begin, SharedData& shared); diff --git a/src/lib/ips4o/local_classification.hpp b/src/lib/ips4o/local_classification.hpp index d8796e857..3f7face0b 100644 --- a/src/lib/ips4o/local_classification.hpp +++ b/src/lib/ips4o/local_classification.hpp @@ -33,6 +33,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +// Modified by B. Buchfink + #pragma once #include "ips4o_fwd.hpp" @@ -66,7 +68,7 @@ typename Cfg::difference_type Sorter::classifyLocally(const iterator my_beg }); // Update bucket sizes to account for partially filled buckets - for (int i = 0, end = num_buckets_; i < end; ++i) + for (typename Cfg::bucket_type i = 0, end = num_buckets_; i < end; ++i) local_.bucket_size[i] += local_.buffers.size(i); return write - begin_; @@ -84,14 +86,14 @@ void Sorter::sequentialClassification(const bool use_equal_buckets) { // Find bucket boundaries diff_t sum = 0; bucket_start_[0] = 0; - for (int i = 0, end = num_buckets_; i < end; ++i) { + for (typename Cfg::bucket_type i = 0, end = num_buckets_; i < end; ++i) { sum += local_.bucket_size[i]; bucket_start_[i + 1] = sum; } IPS4O_ASSUME_NOT(bucket_start_[num_buckets_] != end_ - begin_); // Set write/read pointers for all buckets - for (int bucket = 0, end = num_buckets_; bucket < end; ++bucket) { + for (typename Cfg::bucket_type bucket = 0, end = num_buckets_; bucket < end; ++bucket) { const auto start = Cfg::alignToNextBlock(bucket_start_[bucket]); const auto stop = Cfg::alignToNextBlock(bucket_start_[bucket + 1]); bucket_pointers_[bucket].set( @@ -110,10 +112,10 @@ template void Sorter::parallelClassification(const bool use_equal_buckets) { // Compute stripe for each thread const auto elements_per_thread = static_cast(end_ - begin_) / num_threads_; - const auto my_begin = begin_ + Cfg::alignToNextBlock(my_id_ * elements_per_thread + 0.5); + const auto my_begin = begin_ + Cfg::alignToNextBlock(int64_t(my_id_ * elements_per_thread + 0.5)); const auto my_end = [&] { - auto e = begin_ + Cfg::alignToNextBlock((my_id_ + 1) * elements_per_thread + 0.5); - e = end_ < e ? end_ : e; + auto e = begin_ + std::min(Cfg::alignToNextBlock(int64_t((my_id_ + 1) * elements_per_thread + 0.5)), end_ - begin_); + //e = end_ < e ? end_ : e; return e; }(); @@ -131,7 +133,7 @@ void Sorter::parallelClassification(const bool use_equal_buckets) { // Find bucket boundaries diff_t sum = 0; - for (int i = 0, end = num_buckets_; i < end; ++i) { + for (typename Cfg::bucket_type i = 0, end = num_buckets_; i < end; ++i) { sum += local_.bucket_size[i]; //__atomic_fetch_add(&bucket_start_[i + 1], sum, __ATOMIC_RELAXED); bucket_start_[i + 1] += sum; diff --git a/src/lib/ips4o/memory.hpp b/src/lib/ips4o/memory.hpp index 708bfed75..2747fc25e 100644 --- a/src/lib/ips4o/memory.hpp +++ b/src/lib/ips4o/memory.hpp @@ -239,7 +239,7 @@ struct Sorter::SharedData { std::atomic_ptrdiff_t bucket_start[Cfg::kMaxBuckets + 1]; BucketPointers bucket_pointers[Cfg::kMaxBuckets]; Block* overflow; - int num_buckets; + typename Cfg::bucket_type num_buckets; bool use_equal_buckets; // Classifier for parallel partitioning diff --git a/src/lib/ips4o/parallel.hpp b/src/lib/ips4o/parallel.hpp index 3e94952bd..622675b85 100644 --- a/src/lib/ips4o/parallel.hpp +++ b/src/lib/ips4o/parallel.hpp @@ -33,6 +33,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +// Modified by B. Buchfink + #pragma once #if defined(_REENTRANT) || defined(_OPENMP) @@ -98,7 +100,7 @@ void Sorter::parallelPrimary(const iterator begin, const iterator end, shared.small_tasks.clear(); shared.small_task_index.store(0, std::memory_order_relaxed); // Queues a subtask either as a big task, a small task, or not at all, depending on the size - const auto queueTask = [&shared, max_sequential_size](int i, std::ptrdiff_t offset, int level) { + const auto queueTask = [&shared, max_sequential_size](int64_t i, std::ptrdiff_t offset, int level) { const auto start = offset + shared.bucket_start[i]; const auto stop = offset + shared.bucket_start[i + 1]; if (stop - start > max_sequential_size) { @@ -113,7 +115,7 @@ void Sorter::parallelPrimary(const iterator begin, const iterator end, const auto task = shared.big_tasks.back(); const auto res = partition(begin + task.begin, begin + task.end, shared.bucket_start, &shared, 0, num_threads); - const int num_buckets = std::get<0>(res); + const typename Cfg::bucket_type num_buckets = std::get<0>(res); const bool equal_buckets = std::get<1>(res); shared.big_tasks.pop_back(); diff --git a/src/lib/ips4o/partitioning.hpp b/src/lib/ips4o/partitioning.hpp index 5b9faf78e..4165517b1 100644 --- a/src/lib/ips4o/partitioning.hpp +++ b/src/lib/ips4o/partitioning.hpp @@ -58,7 +58,7 @@ namespace detail { */ template template -std::pair Sorter::partition(const iterator begin, const iterator end, +std::pair Sorter::partition(const iterator begin, const iterator end, std::atomic_ptrdiff_t* const bucket_start, SharedData* const shared, const int my_id, const int num_threads) { @@ -97,7 +97,7 @@ std::pair Sorter::partition(const iterator begin, const iterator sequentialClassification(use_equal_buckets); // Compute which bucket can cause overflow - const int overflow_bucket = computeOverflowBucket(); + const typename Cfg::bucket_type overflow_bucket = computeOverflowBucket(); // Block Permutation if (use_equal_buckets) @@ -115,16 +115,16 @@ std::pair Sorter::partition(const iterator begin, const iterator if (kIsParallel) overflow_ = shared_->overflow; // Distribute buckets among threads - const int num_buckets = num_buckets_; - const int buckets_per_thread = (num_buckets + num_threads_ - 1) / num_threads_; - int my_first_bucket = my_id_ * buckets_per_thread; - int my_last_bucket = (my_id_ + 1) * buckets_per_thread; + const typename Cfg::bucket_type num_buckets = num_buckets_; + const typename Cfg::bucket_type buckets_per_thread = (num_buckets + num_threads_ - 1) / num_threads_; + typename Cfg::bucket_type my_first_bucket = my_id_ * buckets_per_thread; + typename Cfg::bucket_type my_last_bucket = (my_id_ + 1) * buckets_per_thread; my_first_bucket = num_buckets < my_first_bucket ? num_buckets : my_first_bucket; my_last_bucket = num_buckets < my_last_bucket ? num_buckets : my_last_bucket; // Save excess elements at right end of stripe const auto in_swap_buffer = !kIsParallel - ? std::pair(-1, 0) + ? std::pair(-1, 0) : saveMargins(my_last_bucket); if (kIsParallel) shared_->sync.barrier(); diff --git a/src/lib/ips4o/sampling.hpp b/src/lib/ips4o/sampling.hpp index 5354f2198..6f9ba45fc 100644 --- a/src/lib/ips4o/sampling.hpp +++ b/src/lib/ips4o/sampling.hpp @@ -33,6 +33,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +// Modified by B. Buchfink + #pragma once #include @@ -68,13 +70,13 @@ void selectSample(It begin, const It end, * Builds the classifer. */ template -std::pair Sorter::buildClassifier(const iterator begin, +std::pair Sorter::buildClassifier(const iterator begin, const iterator end, Classifier& classifier) { const auto n = end - begin; int log_buckets = Cfg::logBuckets(n); - int num_buckets = 1 << log_buckets; - const auto step = std::max(1, Cfg::oversamplingFactor(n)); + typename Cfg::bucket_type num_buckets = (typename Cfg::bucket_type)1 << log_buckets; + const auto step = std::max(1, (diff_t)Cfg::oversamplingFactor(n)); const auto num_samples = step * num_buckets - 1; // Select the sample @@ -104,9 +106,9 @@ std::pair Sorter::buildClassifier(const iterator begin, && num_buckets - 1 - diff_splitters >= Cfg::kEqualBucketsThreshold; // Fill the array to the next power of two - log_buckets = log2((uint64_t)diff_splitters) + 1; - num_buckets = 1 << log_buckets; - for (int i = diff_splitters + 1; i < num_buckets; ++i) { + log_buckets = (int)log2((uint64_t)diff_splitters) + 1; + num_buckets = (typename Cfg::bucket_type)1 << log_buckets; + for (auto i = diff_splitters + 1; i < num_buckets; ++i) { IPS4O_ASSUME_NOT(sorted_splitters + 1 == nullptr); new (++sorted_splitters) typename Cfg::value_type(*splitter); } @@ -115,7 +117,7 @@ std::pair Sorter::buildClassifier(const iterator begin, classifier.build(log_buckets); this->classifier_ = &classifier; - const int used_buckets = num_buckets * (1 + use_equal_buckets); + const typename Cfg::bucket_type used_buckets = num_buckets * (1 + use_equal_buckets); return {used_buckets, use_equal_buckets}; } diff --git a/src/lib/ips4o/sequential.hpp b/src/lib/ips4o/sequential.hpp index e85cce03d..7f019f700 100644 --- a/src/lib/ips4o/sequential.hpp +++ b/src/lib/ips4o/sequential.hpp @@ -62,7 +62,7 @@ void Sorter::sequential(const iterator begin, const iterator end) { // Do the partitioning const auto res = partition(begin, end, bucket_start, nullptr, 0, 1); - const int num_buckets = std::get<0>(res); + const typename Cfg::bucket_type num_buckets = std::get<0>(res); const bool equal_buckets = std::get<1>(res); // Final base case is executed in cleanup step, so we're done here @@ -71,7 +71,7 @@ void Sorter::sequential(const iterator begin, const iterator end) { } // Recurse - for (int i = 0; i < num_buckets; i += 1 + equal_buckets) { + for (typename Cfg::bucket_type i = 0; i < num_buckets; i += 1 + equal_buckets) { const ptrdiff_t start = bucket_start[i]; const ptrdiff_t stop = bucket_start[i + 1]; if (stop - start > 2 * Cfg::kBaseCaseSize) diff --git a/src/lib/ips4o/thread_pool.hpp b/src/lib/ips4o/thread_pool.hpp index ba6db12c9..335f287f6 100644 --- a/src/lib/ips4o/thread_pool.hpp +++ b/src/lib/ips4o/thread_pool.hpp @@ -33,6 +33,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +// Modified by B. Buchfink + #pragma once #ifdef _OPENMP #include @@ -133,9 +135,9 @@ class StdThreadPool { Sync& sync() { return impl_.get()->sync_; } - int numThreads() const { return impl_.get()->threads_.size() + 1; } + int numThreads() const { return (int)impl_.get()->threads_.size() + 1; } - static int maxNumThreads() { return std::thread::hardware_concurrency(); } + static int maxNumThreads() { return (int)std::thread::hardware_concurrency(); } private: struct Impl { diff --git a/src/lib/mio/detail/mmap.ipp b/src/lib/mio/detail/mmap.ipp index d18cdf5bd..29e74b729 100644 --- a/src/lib/mio/detail/mmap.ipp +++ b/src/lib/mio/detail/mmap.ipp @@ -27,6 +27,7 @@ #include "mio/page.hpp" #include "mio/detail/string_util.hpp" +#include #include #ifndef _WIN32 diff --git a/src/lib/mio/forward.h b/src/lib/mio/forward.h new file mode 100644 index 000000000..d59e58305 --- /dev/null +++ b/src/lib/mio/forward.h @@ -0,0 +1,19 @@ +#pragma once + +namespace mio { + +enum class access_mode +{ + read, + write +}; + +template +struct basic_mmap; + +template +using basic_mmap_source = basic_mmap; + +using mmap_source = basic_mmap_source; + +} \ No newline at end of file diff --git a/src/lib/mio/mmap.hpp b/src/lib/mio/mmap.hpp index bd2044dab..8be801b2e 100644 --- a/src/lib/mio/mmap.hpp +++ b/src/lib/mio/mmap.hpp @@ -24,6 +24,7 @@ #define MIO_MMAP_HEADER #include "mio/page.hpp" +#include "forward.h" #include #include diff --git a/src/lib/mio/page.hpp b/src/lib/mio/page.hpp index cae73775f..a76cf5cf0 100644 --- a/src/lib/mio/page.hpp +++ b/src/lib/mio/page.hpp @@ -18,6 +18,8 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +// Modified by B. Buchfink + #ifndef MIO_PAGE_HEADER #define MIO_PAGE_HEADER @@ -33,11 +35,11 @@ namespace mio { * This is used by `basic_mmap` to determine whether to create a read-only or * a read-write memory mapping. */ -enum class access_mode -{ - read, - write -}; +//enum class access_mode +//{ +// read, +// write +//}; /** * Determines the operating system's page allocation granularity. diff --git a/src/lib/wfa2/alignment/affine2p_penalties.c b/src/lib/wfa2/alignment/affine2p_penalties.c new file mode 100644 index 000000000..4bb0b5293 --- /dev/null +++ b/src/lib/wfa2/alignment/affine2p_penalties.c @@ -0,0 +1,33 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Gap-Affine 2-Pieces penalties + */ + +#include "affine2p_penalties.h" + diff --git a/src/lib/wfa2/alignment/affine2p_penalties.h b/src/lib/wfa2/alignment/affine2p_penalties.h new file mode 100644 index 000000000..ecf60de33 --- /dev/null +++ b/src/lib/wfa2/alignment/affine2p_penalties.h @@ -0,0 +1,61 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Gap-Affine 2-Pieces penalties + */ + +#ifndef AFFINE2P_PENALTIES_H_ +#define AFFINE2P_PENALTIES_H_ + +#include "../utils/commons.h" + +/* + * Affine 2-piece penalties + */ +typedef struct { + int match; // (Penalty representation; usually M <= 0) + int mismatch; // (Penalty representation; usually X > 0) + // Usually concave; Q1 + E1 < Q2 + E2 and E1 > E2. + int gap_opening1; // (Penalty representation; usually O1 > 0) + int gap_extension1; // (Penalty representation; usually E1 > 0) + int gap_opening2; // (Penalty representation; usually O2 > 0) + int gap_extension2; // (Penalty representation; usually E2 > 0) +} affine2p_penalties_t; + +/* + * Affine 2-piece matrix-type (for bcktrace) + */ +typedef enum { + affine2p_matrix_M, + affine2p_matrix_I1, + affine2p_matrix_I2, + affine2p_matrix_D1, + affine2p_matrix_D2 +} affine2p_matrix_type; + +#endif /* AFFINE2P_PENALTIES_H_ */ diff --git a/src/lib/wfa2/alignment/affine_penalties.c b/src/lib/wfa2/alignment/affine_penalties.c new file mode 100644 index 000000000..eca664fee --- /dev/null +++ b/src/lib/wfa2/alignment/affine_penalties.c @@ -0,0 +1,33 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Gap-Affine penalties + */ + +#include "../alignment/affine_penalties.h" + diff --git a/src/lib/wfa2/alignment/affine_penalties.h b/src/lib/wfa2/alignment/affine_penalties.h new file mode 100644 index 000000000..2e4e044b6 --- /dev/null +++ b/src/lib/wfa2/alignment/affine_penalties.h @@ -0,0 +1,56 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Gap-Affine penalties + */ + +#ifndef AFFINE_PENALTIES_H_ +#define AFFINE_PENALTIES_H_ + +#include "../utils/commons.h" + +/* + * Affine penalties + */ +typedef struct { + int match; // (Penalty representation; usually M <= 0) + int mismatch; // (Penalty representation; usually X > 0) + int gap_opening; // (Penalty representation; usually O > 0) + int gap_extension; // (Penalty representation; usually E > 0) +} affine_penalties_t; + +/* + * Affine matrix-type (for backtrace) + */ +typedef enum { + affine_matrix_M, + affine_matrix_I, + affine_matrix_D, +} affine_matrix_type; + +#endif /* AFFINE_PENALTIES_H_ */ diff --git a/src/lib/wfa2/alignment/cigar.c b/src/lib/wfa2/alignment/cigar.c new file mode 100644 index 000000000..555af4582 --- /dev/null +++ b/src/lib/wfa2/alignment/cigar.c @@ -0,0 +1,510 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Edit cigar data-structure (match/mismatch/insertion/deletion) + */ + +#include "cigar.h" + +/* + * Setup + */ +cigar_t* cigar_new( + const int max_operations, + mm_allocator_t* const mm_allocator) { + // Allocate + cigar_t* const cigar = mm_allocator_alloc(mm_allocator,cigar_t); + // Allocate buffer + cigar->max_operations = max_operations; + cigar->operations = mm_allocator_malloc(mm_allocator,cigar->max_operations); + cigar->begin_offset = 0; + cigar->end_offset = 0; + cigar->score = INT32_MIN; + // MM + cigar->mm_allocator = mm_allocator; + // Return + return cigar; +} +void cigar_clear( + cigar_t* const cigar) { + cigar->begin_offset = 0; + cigar->end_offset = 0; + cigar->score = INT32_MIN; +} +void cigar_resize( + cigar_t* const cigar, + const int max_operations) { + // Check maximum operations + if (max_operations > cigar->max_operations) { + cigar->max_operations = max_operations; + mm_allocator_free(cigar->mm_allocator,cigar->operations); // Free + cigar->operations = mm_allocator_malloc( + cigar->mm_allocator,max_operations); // Allocate + } + cigar->begin_offset = 0; + cigar->end_offset = 0; + cigar->score = INT32_MIN; +} +void cigar_free( + cigar_t* const cigar) { + mm_allocator_free(cigar->mm_allocator,cigar->operations); + mm_allocator_free(cigar->mm_allocator,cigar); +} +/* + * Accessors + */ +int cigar_get_matches( + cigar_t* const cigar) { + int i, num_matches=0; + for (i=cigar->begin_offset;iend_offset;++i) { + num_matches += (cigar->operations[i]=='M'); + } + return num_matches; +} +void cigar_add_mismatches( + char* const pattern, + const int pattern_length, + char* const text, + const int text_length, + cigar_t* const cigar) { + // Refine adding mismatches + int i, p=0, t=0; + for (i=cigar->begin_offset;iend_offset;++i) { + // Check limits + if (p >= pattern_length || t >= text_length) break; + switch (cigar->operations[i]) { + case 'M': + cigar->operations[i] = (pattern[p]==text[t]) ? 'M' : 'X'; + ++p; ++t; + break; + case 'I': + ++t; + break; + case 'D': + ++p; + break; + default: + fprintf(stderr,"[CIGAR] Wrong edit operation\n"); + exit(1); + break; + } + } + while (p < pattern_length) { cigar->operations[i++] = 'D'; ++p; }; + while (t < text_length) { cigar->operations[i++] = 'I'; ++t; }; + cigar->end_offset = i; + cigar->operations[cigar->end_offset] = '\0'; + // // DEBUG + // printf("Score=%ld\nPath-length=%" PRIu64 "\nCIGAR=%s\n", + // gaba_alignment->score,gaba_alignment->plen, + // cigar->operations); +} +/* + * Score + */ +int cigar_score_edit( + cigar_t* const cigar) { + int score = 0, i; + for (i=cigar->begin_offset;iend_offset;++i) { + switch (cigar->operations[i]) { + case 'M': break; + case 'X': + case 'D': + case 'I': ++score; break; + default: return INT_MIN; + } + } + return score; +} +int cigar_score_gap_linear( + cigar_t* const cigar, + linear_penalties_t* const penalties) { + int score = 0, i; + for (i=cigar->begin_offset;iend_offset;++i) { + switch (cigar->operations[i]) { + case 'M': score -= penalties->match; break; + case 'X': score -= penalties->mismatch; break; + case 'I': score -= penalties->indel; break; + case 'D': score -= penalties->indel; break; + default: return INT_MIN; + } + } + return score; +} +int cigar_score_gap_affine( + cigar_t* const cigar, + affine_penalties_t* const penalties) { + char last_op = '\0'; + int score = 0, i; + for (i=cigar->begin_offset;iend_offset;++i) { + switch (cigar->operations[i]) { + case 'M': + score -= penalties->match; + break; + case 'X': + score -= penalties->mismatch; + break; + case 'D': + score -= penalties->gap_extension + ((last_op=='D') ? 0 : penalties->gap_opening); + break; + case 'I': + score -= penalties->gap_extension + ((last_op=='I') ? 0 : penalties->gap_opening); + break; + default: + fprintf(stderr,"[CIGAR] Computing CIGAR score: Unknown operation\n"); + exit(1); + } + last_op = cigar->operations[i]; + } + return score; +} +int cigar_score_gap_affine2p_get_operations_score( + const char operation, + const int length, + affine2p_penalties_t* const penalties) { + switch (operation) { + case 'M': + return penalties->match*length; + case 'X': + return penalties->mismatch*length; + case 'D': + case 'I': { + const int score1 = penalties->gap_opening1 + penalties->gap_extension1*length; + const int score2 = penalties->gap_opening2 + penalties->gap_extension2*length; + return MIN(score1,score2); + } + default: + fprintf(stderr,"[CIGAR] Computing CIGAR score: Unknown operation\n"); + exit(1); + } +} +int cigar_score_gap_affine2p( + cigar_t* const cigar, + affine2p_penalties_t* const penalties) { + char last_op = '\0'; + int score = 0, op_length = 0; + int i; + for (i=cigar->begin_offset;iend_offset;++i) { + // Account for operation + if (cigar->operations[i] != last_op && last_op != '\0') { + score -= cigar_score_gap_affine2p_get_operations_score(last_op,op_length,penalties); + op_length = 0; + } + // Add operation + last_op = cigar->operations[i]; + ++op_length; + } + // Account for last operation + score -= cigar_score_gap_affine2p_get_operations_score(last_op,op_length,penalties); + return score; +} +/* + * Utils + */ +int cigar_cmp( + cigar_t* const cigar_a, + cigar_t* const cigar_b) { + // Compare lengths + const int length_cigar_a = cigar_a->end_offset - cigar_a->begin_offset; + const int length_cigar_b = cigar_b->end_offset - cigar_b->begin_offset; + if (length_cigar_a != length_cigar_b) return length_cigar_a - length_cigar_b; + // Compare operations + char* const operations_a = cigar_a->operations + cigar_a->begin_offset; + char* const operations_b = cigar_b->operations + cigar_b->begin_offset; + int i; + for (i=0;imax_operations = cigar_src->max_operations; + cigar_dst->begin_offset = cigar_src->begin_offset; + cigar_dst->end_offset = cigar_src->end_offset; + cigar_dst->score = cigar_src->score; + memcpy(cigar_dst->operations+cigar_src->begin_offset, + cigar_src->operations+cigar_src->begin_offset, + cigar_src->end_offset-cigar_src->begin_offset); +} +void cigar_append( + cigar_t* const cigar_dst, + cigar_t* const cigar_src) { + // Append + const int cigar_length = cigar_src->end_offset - cigar_src->begin_offset; + char* const operations_src = cigar_src->operations + cigar_src->begin_offset; + char* const operations_dst = cigar_dst->operations + cigar_dst->end_offset; + memcpy(operations_dst,operations_src,cigar_length); + // Update offset + cigar_dst->end_offset += cigar_length; +} +void cigar_append_deletion( + cigar_t* const cigar, + const int length) { + // Append deletions + char* const operations = cigar->operations + cigar->end_offset; + int i; + for (i=0;iend_offset += length; +} +void cigar_append_insertion( + cigar_t* const cigar, + const int length) { + // Append insertions + char* const operations = cigar->operations + cigar->end_offset; + int i; + for (i=0;iend_offset += length; +} +bool cigar_check_alignment( + FILE* const stream, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + cigar_t* const cigar, + const bool verbose) { + // Parameters + char* const operations = cigar->operations; + // Traverse CIGAR + int pattern_pos=0, text_pos=0, i; + for (i=cigar->begin_offset;iend_offset;++i) { + switch (operations[i]) { + case 'M': + // Check match + if (pattern[pattern_pos] != text[text_pos]) { + if (verbose) { + fprintf(stream, + "[AlignCheck] Alignment not matching (pattern[%d]=%c != text[%d]=%c)\n", + pattern_pos,pattern[pattern_pos],text_pos,text[text_pos]); + } + return false; + } + ++pattern_pos; + ++text_pos; + break; + case 'X': + // Check mismatch + if (pattern[pattern_pos] == text[text_pos]) { + if (verbose) { + fprintf(stream, + "[AlignCheck] Alignment not mismatching (pattern[%d]=%c == text[%d]=%c)\n", + pattern_pos,pattern[pattern_pos],text_pos,text[text_pos]); + } + return false; + } + ++pattern_pos; + ++text_pos; + break; + case 'I': + ++text_pos; + break; + case 'D': + ++pattern_pos; + break; + default: + fprintf(stderr,"[AlignCheck] Unknown edit operation '%c'\n",operations[i]); + exit(1); + break; + } + } + // Check alignment length + if (pattern_pos != pattern_length) { + if (verbose) { + fprintf(stream, + "[AlignCheck] Alignment incorrect length (pattern-aligned=%d,pattern-length=%d)\n", + pattern_pos,pattern_length); + } + return false; + } + if (text_pos != text_length) { + if (verbose) { + fprintf(stream, + "[AlignCheck] Alignment incorrect length (text-aligned=%d,text-length=%d)\n", + text_pos,text_length); + } + return false; + } + // OK + return true; +} +/* + * Display + */ +void cigar_print( + FILE* const stream, + cigar_t* const cigar, + const bool print_matches) { + // Check null CIGAR + if (cigar->begin_offset >= cigar->end_offset) return; + // Print operations + char last_op = cigar->operations[cigar->begin_offset]; + int last_op_length = 1; + int i; + for (i=cigar->begin_offset+1;iend_offset;++i) { + if (cigar->operations[i]==last_op) { + ++last_op_length; + } else { + if (print_matches || last_op != 'M') { + fprintf(stream,"%d%c",last_op_length,last_op); + } + last_op = cigar->operations[i]; + last_op_length = 1; + } + } + if (print_matches || last_op != 'M') { + fprintf(stream,"%d%c",last_op_length,last_op); + } +} +int cigar_sprint( + char* buffer, + cigar_t* const cigar, + const bool print_matches) { + // Parameters + int pos = 0; + // Check null CIGAR + if (cigar->begin_offset >= cigar->end_offset) { + buffer[pos] = '\0'; + return pos; + } + // Print operations + char last_op = cigar->operations[cigar->begin_offset]; + int last_op_length = 1; + int i; + for (i=cigar->begin_offset+1;iend_offset;++i) { + if (cigar->operations[i]==last_op) { + ++last_op_length; + } else { + if (print_matches || last_op != 'M') { + pos += sprintf(buffer+pos,"%d%c",last_op_length,last_op); + } + last_op = cigar->operations[i]; + last_op_length = 1; + } + } + if (print_matches || last_op != 'M') { + pos += sprintf(buffer+pos,"%d%c",last_op_length,last_op); + } + // Return + buffer[pos] = '\0'; + return pos; +} +void cigar_print_pretty( + FILE* const stream, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + cigar_t* const cigar, + mm_allocator_t* const mm_allocator) { + // Parameters + char* const operations = cigar->operations; + // Allocate alignment buffers + const int max_buffer_length = text_length+pattern_length+1; + char* const pattern_alg = mm_allocator_calloc(mm_allocator,max_buffer_length,char,true); + char* const ops_alg = mm_allocator_calloc(mm_allocator,max_buffer_length,char,true); + char* const text_alg = mm_allocator_calloc(mm_allocator,max_buffer_length,char,true); + // Compute alignment buffers + int i, alg_pos = 0, pattern_pos = 0, text_pos = 0; + for (i=cigar->begin_offset;iend_offset;++i) { + switch (operations[i]) { + case 'M': + if (pattern[pattern_pos] != text[text_pos]) { + pattern_alg[alg_pos] = pattern[pattern_pos]; + ops_alg[alg_pos] = 'X'; + text_alg[alg_pos++] = text[text_pos]; + } else { + pattern_alg[alg_pos] = pattern[pattern_pos]; + ops_alg[alg_pos] = '|'; + text_alg[alg_pos++] = text[text_pos]; + } + pattern_pos++; text_pos++; + break; + case 'X': + if (pattern[pattern_pos] != text[text_pos]) { + pattern_alg[alg_pos] = pattern[pattern_pos++]; + ops_alg[alg_pos] = ' '; + text_alg[alg_pos++] = text[text_pos++]; + } else { + pattern_alg[alg_pos] = pattern[pattern_pos++]; + ops_alg[alg_pos] = 'X'; + text_alg[alg_pos++] = text[text_pos++]; + } + break; + case 'I': + pattern_alg[alg_pos] = '-'; + ops_alg[alg_pos] = ' '; + text_alg[alg_pos++] = text[text_pos++]; + break; + case 'D': + pattern_alg[alg_pos] = pattern[pattern_pos++]; + ops_alg[alg_pos] = ' '; + text_alg[alg_pos++] = '-'; + break; + default: + break; + } + } + i=0; + while (pattern_pos < pattern_length) { + pattern_alg[alg_pos+i] = pattern[pattern_pos++]; + ops_alg[alg_pos+i] = '?'; + ++i; + } + i=0; + while (text_pos < text_length) { + text_alg[alg_pos+i] = text[text_pos++]; + ops_alg[alg_pos+i] = '?'; + ++i; + } + // Print alignment pretty + fprintf(stream," ALIGNMENT\t"); + cigar_print(stderr,cigar,true); + fprintf(stream,"\n"); + fprintf(stream," ALIGNMENT.COMPACT\t"); + cigar_print(stderr,cigar,false); + fprintf(stream,"\n"); + fprintf(stream," PATTERN %s\n",pattern_alg); + fprintf(stream," %s\n",ops_alg); + fprintf(stream," TEXT %s\n",text_alg); + // Free + mm_allocator_free(mm_allocator,pattern_alg); + mm_allocator_free(mm_allocator,ops_alg); + mm_allocator_free(mm_allocator,text_alg); +} + + diff --git a/src/lib/wfa2/alignment/cigar.h b/src/lib/wfa2/alignment/cigar.h new file mode 100644 index 000000000..01938a9cd --- /dev/null +++ b/src/lib/wfa2/alignment/cigar.h @@ -0,0 +1,146 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Cigar data-structure (match/mismatch/insertion/deletion) + */ + +#ifndef CIGAR_H_ +#define CIGAR_H_ + +#include "../utils/commons.h" +#include "../system/mm_allocator.h" +#include "../alignment/linear_penalties.h" +#include "../alignment/affine_penalties.h" +#include "../alignment/affine2p_penalties.h" + +/* + * CIGAR + */ +typedef struct { + // Operations buffer + char* operations; + int max_operations; + int begin_offset; + int end_offset; + // Score + int score; + // MM + mm_allocator_t* mm_allocator; +} cigar_t; + +/* + * Setup + */ +cigar_t* cigar_new( + const int max_operations, + mm_allocator_t* const mm_allocator); +void cigar_clear( + cigar_t* const cigar); +void cigar_resize( + cigar_t* const cigar, + const int max_operations); +void cigar_free( + cigar_t* const cigar); + +/* + * Accessors + */ +int cigar_get_matches( + cigar_t* const cigar); +void cigar_add_mismatches( + char* const pattern, + const int pattern_length, + char* const text, + const int text_length, + cigar_t* const cigar); + +/* + * Score + */ +int cigar_score_edit( + cigar_t* const cigar); +int cigar_score_gap_linear( + cigar_t* const cigar, + linear_penalties_t* const penalties); +int cigar_score_gap_affine( + cigar_t* const cigar, + affine_penalties_t* const penalties); +int cigar_score_gap_affine2p( + cigar_t* const cigar, + affine2p_penalties_t* const penalties); + +/* + * Utils + */ +int cigar_cmp( + cigar_t* const cigar_a, + cigar_t* const cigar_b); +void cigar_copy( + cigar_t* const cigar_dst, + cigar_t* const cigar_src); + +void cigar_append( + cigar_t* const cigar_dst, + cigar_t* const cigar_src); +void cigar_append_deletion( + cigar_t* const cigar, + const int length); +void cigar_append_insertion( + cigar_t* const cigar, + const int length); + +bool cigar_check_alignment( + FILE* const stream, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + cigar_t* const cigar, + const bool verbose); + +/* + * Display + */ +void cigar_print( + FILE* const stream, + cigar_t* const cigar, + const bool print_matches); +int cigar_sprint( + char* buffer, + cigar_t* const cigar, + const bool print_matches); +void cigar_print_pretty( + FILE* const stream, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + cigar_t* const cigar, + mm_allocator_t* const mm_allocator); + +#endif /* CIGAR_H_ */ diff --git a/src/lib/wfa2/alignment/linear_penalties.h b/src/lib/wfa2/alignment/linear_penalties.h new file mode 100644 index 000000000..ef1063e26 --- /dev/null +++ b/src/lib/wfa2/alignment/linear_penalties.h @@ -0,0 +1,41 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Gap-linear penalties + */ + +#ifndef LINEAR_PENALTIES_H_ +#define LINEAR_PENALTIES_H_ + +typedef struct { + int match; // (Penalty representation; usually M <= 0) + int mismatch; // (Penalty representation; usually X > 0) + int indel; // (Penalty representation; usually I > 0) +} linear_penalties_t; + +#endif /* LINEAR_PENALTIES_H_ */ diff --git a/src/lib/wfa2/alignment/score_matrix.c b/src/lib/wfa2/alignment/score_matrix.c new file mode 100644 index 000000000..e6b7caeec --- /dev/null +++ b/src/lib/wfa2/alignment/score_matrix.c @@ -0,0 +1,120 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Score matrix for alignment using dynamic programming + */ + +#include "score_matrix.h" + +/* + * Setup + */ +void score_matrix_allocate( + score_matrix_t* const score_matrix, + const int num_rows, + const int num_columns, + mm_allocator_t* const mm_allocator) { + // Allocate DP matrix + int h; + score_matrix->num_rows = num_rows; + score_matrix->num_columns = num_columns; + score_matrix->columns = mm_allocator_malloc(mm_allocator,num_columns*sizeof(int*)); // Columns + for (h=0;hcolumns[h] = mm_allocator_calloc(mm_allocator,num_rows,int,false); // Rows + } + // MM + score_matrix->mm_allocator = mm_allocator; +} +void score_matrix_free( + score_matrix_t* const score_matrix) { + // Parameters + mm_allocator_t* const mm_allocator = score_matrix->mm_allocator; + // DP matrix + const int num_columns = score_matrix->num_columns; + int h; + for (h=0;hcolumns[h]); + } + mm_allocator_free(mm_allocator,score_matrix->columns); +} +/* + * Display + */ +void score_matrix_print_score( + FILE* const stream, + const int score) { + if (-1 < score && score < 10000) { + fprintf(stream," %3d ",score); + } else { + fprintf(stream," * "); + } +} +void score_matrix_print_char( + FILE* const stream, + const char c) { + fprintf(stream," %c ",c); +} +void score_matrix_print( + FILE* const stream, + const score_matrix_t* const score_matrix, + const char* const pattern, + const char* const text) { + // Parameters + int** const matrix = score_matrix->columns; + const int num_columns = score_matrix->num_columns; + const int num_rows = score_matrix->num_rows; + int h; + // Print Header + fprintf(stream," "); + for (h=0;h + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Score matrix for alignment using dynamic programming + */ + +#ifndef SCORE_MATRIX_H_ +#define SCORE_MATRIX_H_ + +#include "../utils/commons.h" +#include "../system/mm_allocator.h" +#include "../alignment/cigar.h" + +/* + * Constants + */ +#define SCORE_MAX (10000000) + +/* + * Score Matrix + */ +typedef struct { + // Score Columns + int** columns; + int num_rows; + int num_columns; + // MM + mm_allocator_t* mm_allocator; +} score_matrix_t; + +/* + * Setup + */ +void score_matrix_allocate( + score_matrix_t* const score_matrix, + const int num_rows, + const int num_columns, + mm_allocator_t* const mm_allocator); +void score_matrix_free( + score_matrix_t* const score_matrix); + +/* + * Display + */ +void score_matrix_print( + FILE* const stream, + const score_matrix_t* const score_matrix, + const char* const pattern, + const char* const text); + +#endif /* SCORE_MATRIX_H_ */ diff --git a/src/lib/wfa2/bindings/cpp/WFAligner.cpp b/src/lib/wfa2/bindings/cpp/WFAligner.cpp new file mode 100644 index 000000000..b3f76c220 --- /dev/null +++ b/src/lib/wfa2/bindings/cpp/WFAligner.cpp @@ -0,0 +1,361 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: C++ bindings for the WaveFront Alignment modules + */ + +#include "WFAligner.hpp" + +extern "C" { + #include "../../wavefront/wavefront_align.h" +} + +/* + * Namespace + */ +namespace wfa { + +/* + * General Wavefront Aligner + */ +WFAligner::WFAligner( + const AlignmentScope alignmentScope, + const MemoryModel memoryModel) { + this->attributes = wavefront_aligner_attr_default; + switch (memoryModel) { + case MemoryHigh: this->attributes.memory_mode = wavefront_memory_high; break; + case MemoryMed: this->attributes.memory_mode = wavefront_memory_med; break; + case MemoryLow: this->attributes.memory_mode = wavefront_memory_low; break; + case MemoryUltralow: this->attributes.memory_mode = wavefront_memory_ultralow; break; + default: this->attributes.memory_mode = wavefront_memory_high; break; + } + this->attributes.alignment_scope = (alignmentScope==Score) ? compute_score : compute_alignment; + //this->attributes.system.verbose = 2; + this->wfAligner = nullptr; +} +WFAligner::~WFAligner() { + wavefront_aligner_delete(wfAligner); +} +/* + * Align End-to-end + */ +WFAligner::AlignmentStatus WFAligner::alignEnd2EndLambda( + const int patternLength, + const int textLength) { + // Configure + wavefront_aligner_set_alignment_end_to_end(wfAligner); + // Align (using custom matching function) + return (WFAligner::AlignmentStatus) wavefront_align(wfAligner,NULL,patternLength,NULL,textLength); +} +WFAligner::AlignmentStatus WFAligner::alignEnd2End( + const char* const pattern, + const int patternLength, + const char* const text, + const int textLength) { + // Configure + wavefront_aligner_set_alignment_end_to_end(wfAligner); + // Align + return (WFAligner::AlignmentStatus) wavefront_align(wfAligner,pattern,patternLength,text,textLength); +} +WFAligner::AlignmentStatus WFAligner::alignEnd2End( + std::string& pattern, + std::string& text) { + // Delegate + return alignEnd2End(pattern.c_str(),pattern.length(),text.c_str(),text.length()); +} +/* + * Align Ends-free + */ +WFAligner::AlignmentStatus WFAligner::alignEndsFreeLambda( + const int patternLength, + const int patternBeginFree, + const int patternEndFree, + const int textLength, + const int textBeginFree, + const int textEndFree) { + // Configure + wavefront_aligner_set_alignment_free_ends(wfAligner, + patternBeginFree,patternEndFree, + textBeginFree,textEndFree); + // Align (using custom matching function) + return (WFAligner::AlignmentStatus) wavefront_align(wfAligner,NULL,patternLength,NULL,textLength); +} +WFAligner::AlignmentStatus WFAligner::alignEndsFree( + const char* const pattern, + const int patternLength, + const int patternBeginFree, + const int patternEndFree, + const char* const text, + const int textLength, + const int textBeginFree, + const int textEndFree) { + // Configure + wavefront_aligner_set_alignment_free_ends(wfAligner, + patternBeginFree,patternEndFree, + textBeginFree,textEndFree); + // Align + return (WFAligner::AlignmentStatus) wavefront_align(wfAligner,pattern,patternLength,text,textLength); +} +WFAligner::AlignmentStatus WFAligner::alignEndsFree( + std::string& pattern, + const int patternBeginFree, + const int patternEndFree, + std::string& text, + const int textBeginFree, + const int textEndFree) { + // Delegate + return alignEndsFree( + pattern.c_str(),pattern.length(), + patternBeginFree,patternEndFree, + text.c_str(),text.length(), + textBeginFree,textEndFree); +} +/* + * Alignment resume + */ +WFAligner::AlignmentStatus WFAligner::alignResume() { + // Resume alignment + return (WFAligner::AlignmentStatus) wavefront_align_resume(wfAligner); +} +/* + * Heuristics + */ +void WFAligner::setHeuristicNone() { + wavefront_aligner_set_heuristic_none(wfAligner); +} +void WFAligner::setHeuristicBandedStatic( + const int band_min_k, + const int band_max_k) { + wavefront_aligner_set_heuristic_banded_static( + wfAligner,band_min_k,band_max_k); +} +void WFAligner::setHeuristicBandedAdaptive( + const int band_min_k, + const int band_max_k, + const int steps_between_cutoffs) { + wavefront_aligner_set_heuristic_banded_adaptive( + wfAligner,band_min_k,band_max_k,steps_between_cutoffs); +} +void WFAligner::setHeuristicWFadaptive( + const int min_wavefront_length, + const int max_distance_threshold, + const int steps_between_cutoffs) { + wavefront_aligner_set_heuristic_wfadaptive( + wfAligner,min_wavefront_length, + max_distance_threshold,steps_between_cutoffs); +} +void WFAligner::setHeuristicXDrop( + const int xdrop, + const int steps_between_cutoffs) { + wavefront_aligner_set_heuristic_xdrop( + wfAligner,xdrop,steps_between_cutoffs); +} +void WFAligner::setHeuristicZDrop( + const int zdrop, + const int steps_between_cutoffs) { + wavefront_aligner_set_heuristic_zdrop( + wfAligner,zdrop,steps_between_cutoffs); +} +/* + * Custom extend-match function (lambda) + */ +void WFAligner::setMatchFunct( + int (*matchFunct)(int,int,void*), + void* matchFunctArguments) { + wavefront_aligner_set_match_funct(wfAligner,matchFunct,matchFunctArguments); +} +/* + * Limits + */ +void WFAligner::setMaxAlignmentScore( + const int maxAlignmentScore) { + wavefront_aligner_set_max_alignment_score( + wfAligner,maxAlignmentScore); +} +void WFAligner::setMaxMemory( + const uint64_t maxMemoryResident, + const uint64_t maxMemoryAbort) { + wavefront_aligner_set_max_memory(wfAligner,maxMemoryResident,maxMemoryAbort); +} +/* + * Accessors + */ +int WFAligner::getAlignmentScore() { + return wfAligner->cigar->score; +} +int WFAligner::getAlignmentStatus() { + return wfAligner->align_status.status; +} +void WFAligner::getAlignmentCigar( + char** const cigarOperations, + int* cigarLength) { + *cigarOperations = wfAligner->cigar->operations + wfAligner->cigar->begin_offset; + *cigarLength = wfAligner->cigar->end_offset - wfAligner->cigar->begin_offset; +} +std::string WFAligner::getAlignmentCigar() { + // Fetch CIGAR + char* buffer; + int bufferLength; + getAlignmentCigar(&buffer,&bufferLength); + // Create string and return + return std::string(buffer,bufferLength); +} +/* + * Misc + */ +char* WFAligner::strError( + const int wfErrorCode) { + return wavefront_align_strerror(wfErrorCode); +} +void WFAligner::setVerbose( + const int verbose) { + wfAligner->system.verbose = verbose; +} +/* + * Indel Aligner (a.k.a Longest Common Subsequence - LCS) + */ +WFAlignerIndel::WFAlignerIndel( + const AlignmentScope alignmentScope, + const MemoryModel memoryModel) : + WFAligner(alignmentScope,memoryModel) { + attributes.distance_metric = indel; + wfAligner = wavefront_aligner_new(&attributes); +} +/* + * Edit Aligner (a.k.a Levenshtein) + */ +WFAlignerEdit::WFAlignerEdit( + const AlignmentScope alignmentScope, + const MemoryModel memoryModel) : + WFAligner(alignmentScope,memoryModel) { + attributes.distance_metric = edit; + wfAligner = wavefront_aligner_new(&attributes); +} +/* + * Gap-Linear Aligner (a.k.a Needleman-Wunsch) + */ +WFAlignerGapLinear::WFAlignerGapLinear( + const int mismatch, + const int indel, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel) : + WFAligner(alignmentScope,memoryModel) { + attributes.distance_metric = gap_linear; + attributes.linear_penalties.match = 0; + attributes.linear_penalties.mismatch = mismatch; + attributes.linear_penalties.indel = indel; + wfAligner = wavefront_aligner_new(&attributes); +} +WFAlignerGapLinear::WFAlignerGapLinear( + const int match, + const int mismatch, + const int indel, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel) : + WFAligner(alignmentScope,memoryModel) { + attributes.distance_metric = gap_linear; + attributes.linear_penalties.match = match; + attributes.linear_penalties.mismatch = mismatch; + attributes.linear_penalties.indel = indel; + wfAligner = wavefront_aligner_new(&attributes); +} +/* + * Gap-Affine Aligner (a.k.a Smith-Waterman-Gotoh) + */ +WFAlignerGapAffine::WFAlignerGapAffine( + const int mismatch, + const int gapOpening, + const int gapExtension, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel) : + WFAligner(alignmentScope,memoryModel) { + attributes.distance_metric = gap_affine; + attributes.affine_penalties.match = 0; + attributes.affine_penalties.mismatch = mismatch; + attributes.affine_penalties.gap_opening = gapOpening; + attributes.affine_penalties.gap_extension = gapExtension; + wfAligner = wavefront_aligner_new(&attributes); +} +WFAlignerGapAffine::WFAlignerGapAffine( + const int match, + const int mismatch, + const int gapOpening, + const int gapExtension, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel) : + WFAligner(alignmentScope,memoryModel) { + attributes.distance_metric = gap_affine; + attributes.affine_penalties.match = match; + attributes.affine_penalties.mismatch = mismatch; + attributes.affine_penalties.gap_opening = gapOpening; + attributes.affine_penalties.gap_extension = gapExtension; + wfAligner = wavefront_aligner_new(&attributes); +} +/* + * Gap-Affine Dual-Cost Aligner (a.k.a. concave 2-pieces) + */ +WFAlignerGapAffine2Pieces::WFAlignerGapAffine2Pieces( + const int mismatch, + const int gapOpening1, + const int gapExtension1, + const int gapOpening2, + const int gapExtension2, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel) : + WFAligner(alignmentScope,memoryModel) { + attributes.distance_metric = gap_affine_2p; + attributes.affine2p_penalties.match = 0; + attributes.affine2p_penalties.mismatch = mismatch; + attributes.affine2p_penalties.gap_opening1 = gapOpening1; + attributes.affine2p_penalties.gap_extension1 = gapExtension1; + attributes.affine2p_penalties.gap_opening2 = gapOpening2; + attributes.affine2p_penalties.gap_extension2 = gapExtension2; + wfAligner = wavefront_aligner_new(&attributes); +} +WFAlignerGapAffine2Pieces::WFAlignerGapAffine2Pieces( + const int match, + const int mismatch, + const int gapOpening1, + const int gapExtension1, + const int gapOpening2, + const int gapExtension2, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel) : + WFAligner(alignmentScope,memoryModel) { + attributes.distance_metric = gap_affine_2p; + attributes.affine2p_penalties.match = match; + attributes.affine2p_penalties.mismatch = mismatch; + attributes.affine2p_penalties.gap_opening1 = gapOpening1; + attributes.affine2p_penalties.gap_extension1 = gapExtension1; + attributes.affine2p_penalties.gap_opening2 = gapOpening2; + attributes.affine2p_penalties.gap_extension2 = gapExtension2; + wfAligner = wavefront_aligner_new(&attributes); +} + +} /* namespace wfa */ + diff --git a/src/lib/wfa2/bindings/cpp/WFAligner.hpp b/src/lib/wfa2/bindings/cpp/WFAligner.hpp new file mode 100644 index 000000000..0fc8ae597 --- /dev/null +++ b/src/lib/wfa2/bindings/cpp/WFAligner.hpp @@ -0,0 +1,238 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: C++ bindings for the WaveFront Alignment modules + */ + +#ifndef BINDINGS_CPP_WFALIGNER_HPP_ +#define BINDINGS_CPP_WFALIGNER_HPP_ + +#include + +extern "C" { + #include "../../wavefront/wavefront_aligner.h" +} + +/* + * Namespace + */ +namespace wfa { + +/* + * General Wavefront Aligner + */ +class WFAligner { +public: + // Configuration + enum MemoryModel { + MemoryHigh, + MemoryMed, + MemoryLow, + MemoryUltralow, + }; + enum AlignmentScope { + Score, + Alignment, + }; + enum AlignmentStatus { + StatusSuccessful = WF_STATUS_SUCCESSFUL, + StatusUnfeasible = WF_STATUS_UNFEASIBLE, + StatusMaxScoreReached = WF_STATUS_MAX_SCORE_REACHED, + StatusOOM = WF_STATUS_OOM, + }; + // Align End-to-end + AlignmentStatus alignEnd2EndLambda( + const int patternLength, + const int textLength); + AlignmentStatus alignEnd2End( + const char* const pattern, + const int patternLength, + const char* const text, + const int textLength); + AlignmentStatus alignEnd2End( + std::string& pattern, + std::string& text); + // Align Ends-free + AlignmentStatus alignEndsFreeLambda( + const int patternLength, + const int patternBeginFree, + const int patternEndFree, + const int textLength, + const int textBeginFree, + const int textEndFree); + AlignmentStatus alignEndsFree( + const char* const pattern, + const int patternLength, + const int patternBeginFree, + const int patternEndFree, + const char* const text, + const int textLength, + const int textBeginFree, + const int textEndFree); + AlignmentStatus alignEndsFree( + std::string& pattern, + const int patternBeginFree, + const int patternEndFree, + std::string& text, + const int textBeginFree, + const int textEndFree); + // Alignment resume + AlignmentStatus alignResume(); + // Heuristics + void setHeuristicNone(); + void setHeuristicBandedStatic( + const int band_min_k, + const int band_max_k); + void setHeuristicBandedAdaptive( + const int band_min_k, + const int band_max_k, + const int steps_between_cutoffs = 1); + void setHeuristicWFadaptive( + const int min_wavefront_length, + const int max_distance_threshold, + const int steps_between_cutoffs = 1); + void setHeuristicXDrop( + const int xdrop, + const int steps_between_cutoffs = 1); + void setHeuristicZDrop( + const int zdrop, + const int steps_between_cutoffs = 1); + // Custom extend-match function (lambda) + void setMatchFunct( + int (*matchFunct)(int,int,void*), + void* matchFunctArguments); + // Limits + void setMaxAlignmentScore( + const int maxAlignmentScore); + void setMaxMemory( + const uint64_t maxMemoryResident, + const uint64_t maxMemoryAbort); + // Accessors + int getAlignmentScore(); + int getAlignmentStatus(); + void getAlignmentCigar( + char** const cigarOperations, + int* cigarLength); + std::string getAlignmentCigar(); + // Misc + char* strError( + const int wfErrorCode); + void setVerbose( + const int verbose); +protected: + wavefront_aligner_attr_t attributes; + wavefront_aligner_t* wfAligner; + // Setup + WFAligner( + const AlignmentScope alignmentScope, + const MemoryModel memoryModel = MemoryHigh); + ~WFAligner(); +private: + WFAligner(const WFAligner&); +}; +/* + * Indel Aligner (a.k.a Longest Common Subsequence - LCS) + */ +class WFAlignerIndel : public WFAligner { +public: + WFAlignerIndel( + const AlignmentScope alignmentScope, + const MemoryModel memoryModel = MemoryHigh); +}; +/* + * Edit Aligner (a.k.a Levenshtein) + */ +class WFAlignerEdit : public WFAligner { +public: + WFAlignerEdit( + const AlignmentScope alignmentScope, + const MemoryModel memoryModel = MemoryHigh); +}; +/* + * Gap-Linear Aligner (a.k.a Needleman-Wunsch) + */ +class WFAlignerGapLinear : public WFAligner { +public: + WFAlignerGapLinear( + const int mismatch, + const int indel, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel = MemoryHigh); + WFAlignerGapLinear( + const int match, + const int mismatch, + const int indel, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel = MemoryHigh); +}; +/* + * Gap-Affine Aligner (a.k.a Smith-Waterman-Gotoh) + */ +class WFAlignerGapAffine : public WFAligner { +public: + WFAlignerGapAffine( + const int mismatch, + const int gapOpening, + const int gapExtension, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel = MemoryHigh); + WFAlignerGapAffine( + const int match, + const int mismatch, + const int gapOpening, + const int gapExtension, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel = MemoryHigh); +}; +/* + * Gap-Affine Dual-Cost Aligner (a.k.a. concave 2-pieces) + */ +class WFAlignerGapAffine2Pieces : public WFAligner { +public: + WFAlignerGapAffine2Pieces( + const int mismatch, + const int gapOpening1, + const int gapExtension1, + const int gapOpening2, + const int gapExtension2, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel = MemoryHigh); + WFAlignerGapAffine2Pieces( + const int match, + const int mismatch, + const int gapOpening1, + const int gapExtension1, + const int gapOpening2, + const int gapExtension2, + const AlignmentScope alignmentScope, + const MemoryModel memoryModel = MemoryHigh); +}; + +} /* namespace wfa */ + +#endif /* BINDINGS_CPP_WFALIGNER_HPP_ */ diff --git a/src/lib/wfa2/system/mm_allocator.c b/src/lib/wfa2/system/mm_allocator.c new file mode 100644 index 000000000..3c5ff5712 --- /dev/null +++ b/src/lib/wfa2/system/mm_allocator.c @@ -0,0 +1,616 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * VERSION: v21.02.15 + * DESCRIPTION: Simple managed-memory allocator that reduces the overhead + * of using malloc/calloc/free functions by allocating slabs of memory + * and dispatching memory segments in order. + */ + +#include "mm_allocator.h" + +/* + * Debug + */ +//#define MM_ALLOCATOR_FORCE_MALLOC /* Delegate all requests to malloc within the mm-allocator handler */ +//#define MM_ALLOCATOR_DISABLE /* Completely disable the mm-allocator (delegate to raw malloc) */ + +/* + * Constants + */ +#define MM_ALLOCATOR_SEGMENT_INITIAL_REQUESTS 10000 +#define MM_ALLOCATOR_INITIAL_SEGMENTS 10 +#define MM_ALLOCATOR_INITIAL_MALLOC_REQUESTS 10 +#define MM_ALLOCATOR_INITIAL_STATES 10 + +/* + * Allocator Segments Freed Cond + */ +#define MM_ALLOCATOR_FREED_FLAG 0x80000000ul +#define MM_ALLOCATOR_REQUEST_IS_FREE(request) ((request)->size & MM_ALLOCATOR_FREED_FLAG) +#define MM_ALLOCATOR_REQUEST_SET_FREE(request) ((request)->size |= MM_ALLOCATOR_FREED_FLAG) +#define MM_ALLOCATOR_REQUEST_SIZE(request) ((request)->size & ~(MM_ALLOCATOR_FREED_FLAG)) + +/* + * Reference (Header of every memory allocated) + */ +typedef struct { + uint32_t segment_idx; + uint32_t request_idx; +} mm_allocator_reference_t; +/* + * Memory Request + */ +typedef struct { + // Request + uint32_t offset; + uint32_t size; + // Log +#ifdef MM_ALLOCATOR_LOG + uint64_t timestamp; + char* func_name; + uint64_t line_no; +#endif +} mm_allocator_request_t; +typedef struct { + // Request + void* mem; + uint64_t size; + // Log +#ifdef MM_ALLOCATOR_LOG + uint64_t timestamp; + char* func_name; + uint64_t line_no; +#endif + // MM Reference + mm_allocator_reference_t* reference; +} mm_malloc_request_t; +/* + * Memory Segments + */ +typedef struct { + // Index (ID) + uint64_t idx; // Index in the segments vector + // Memory + uint64_t size; // Total memory available + void* memory; // Memory + uint64_t used; // Bytes used (offset to memory next free byte) + // Requests + vector_t* requests; // Memory requests (mm_allocator_request_t) +} mm_allocator_segment_t; + +/* + * Segments + */ +mm_allocator_segment_t* mm_allocator_segment_new( + mm_allocator_t* const mm_allocator) { + // Allocate handler + mm_allocator_segment_t* const segment = (mm_allocator_segment_t*) malloc(sizeof(mm_allocator_segment_t)); + // Index + const uint64_t segment_idx = vector_get_used(mm_allocator->segments); + segment->idx = segment_idx; + // Memory + segment->size = mm_allocator->segment_size; + segment->memory = malloc(mm_allocator->segment_size); + segment->used = 0; + // Requests + segment->requests = vector_new(MM_ALLOCATOR_SEGMENT_INITIAL_REQUESTS,mm_allocator_request_t); + // Add to segments + vector_insert(mm_allocator->segments,segment,mm_allocator_segment_t*); + // Return + return segment; +} +void mm_allocator_segment_clear( + mm_allocator_segment_t* const segment) { + segment->used = 0; + vector_clear(segment->requests); +} +void mm_allocator_segment_delete( + mm_allocator_segment_t* const segment) { + vector_delete(segment->requests); + free(segment->memory); + free(segment); +} +mm_allocator_request_t* mm_allocator_segment_get_request( + mm_allocator_segment_t* const segment, + const uint64_t request_idx) { + return vector_get_elm(segment->requests,request_idx,mm_allocator_request_t); +} +uint64_t mm_allocator_segment_get_num_requests( + mm_allocator_segment_t* const segment) { + return vector_get_used(segment->requests); +} +/* + * Setup + */ +mm_allocator_t* mm_allocator_new( + const uint64_t segment_size) { + // Allocate handler + mm_allocator_t* const mm_allocator = (mm_allocator_t*) malloc(sizeof(mm_allocator_t)); + mm_allocator->request_ticker = 0; + // Segments + mm_allocator->segment_size = segment_size; + mm_allocator->segments = vector_new(MM_ALLOCATOR_INITIAL_SEGMENTS,mm_allocator_segment_t*); + mm_allocator->segments_free = vector_new(MM_ALLOCATOR_INITIAL_SEGMENTS,mm_allocator_segment_t*); + // Allocate an initial segment +#ifndef MM_ALLOCATOR_FORCE_MALLOC +#ifndef MM_ALLOCATOR_DISABLE + mm_allocator_segment_new(mm_allocator); +#endif +#endif + mm_allocator->current_segment_idx = 0; + // Malloc Memory + mm_allocator->malloc_requests = vector_new(MM_ALLOCATOR_INITIAL_MALLOC_REQUESTS,mm_malloc_request_t); + mm_allocator->malloc_requests_freed = 0; + // Return + return mm_allocator; +} +void mm_allocator_clear( + mm_allocator_t* const mm_allocator) { + // Clear segments + vector_clear(mm_allocator->segments_free); + VECTOR_ITERATE(mm_allocator->segments,segment_ptr,p,mm_allocator_segment_t*) { + mm_allocator_segment_clear(*segment_ptr); // Clear segment + vector_insert(mm_allocator->segments_free,*segment_ptr,mm_allocator_segment_t*); // Add to free segments + } + mm_allocator->current_segment_idx = 0; + // Clear malloc memory + VECTOR_ITERATE(mm_allocator->malloc_requests,malloc_request,m,mm_malloc_request_t) { + if (malloc_request->size > 0) free(malloc_request->mem); // Free malloc requests + } + vector_clear(mm_allocator->malloc_requests); + mm_allocator->malloc_requests_freed = 0; +} +void mm_allocator_delete( + mm_allocator_t* const mm_allocator) { + // Free segments + VECTOR_ITERATE(mm_allocator->segments,segment_ptr,p,mm_allocator_segment_t*) { + mm_allocator_segment_delete(*segment_ptr); + } + vector_delete(mm_allocator->segments); + vector_delete(mm_allocator->segments_free); + // Free malloc memory + VECTOR_ITERATE(mm_allocator->malloc_requests,malloc_request,m,mm_malloc_request_t) { + if (malloc_request->size > 0) free(malloc_request->mem); // Free malloc requests + } + vector_delete(mm_allocator->malloc_requests); + // Free handler + free(mm_allocator); +} +/* + * Accessors + */ +mm_allocator_segment_t* mm_allocator_get_segment( + mm_allocator_t* const mm_allocator, + const uint64_t segment_idx) { + return *(vector_get_elm(mm_allocator->segments,segment_idx,mm_allocator_segment_t*)); +} +mm_allocator_segment_t* mm_allocator_get_segment_free( + mm_allocator_t* const mm_allocator, + const uint64_t segment_idx) { + return *(vector_get_elm(mm_allocator->segments_free,segment_idx,mm_allocator_segment_t*)); +} +uint64_t mm_allocator_get_num_segments( + mm_allocator_t* const mm_allocator) { + return vector_get_used(mm_allocator->segments); +} +uint64_t mm_allocator_get_num_segments_free( + mm_allocator_t* const mm_allocator) { + return vector_get_used(mm_allocator->segments_free); +} +/* + * Allocate + */ +mm_allocator_segment_t* mm_allocator_fetch_segment( + mm_allocator_t* const mm_allocator, + const uint64_t num_bytes) { + // Fetch current segment + mm_allocator_segment_t* const curr_segment = + mm_allocator_get_segment(mm_allocator,mm_allocator->current_segment_idx); + // Check overall segment size + if (num_bytes > curr_segment->size/2) { // Never buy anything you cannot afford twice + return NULL; // Memory request over max-request size + } + // Check available segment size + if (curr_segment->used + num_bytes <= curr_segment->size) { + return curr_segment; + } + // Check overall segment size + if (num_bytes > curr_segment->size) { + return NULL; // Memory request over segment size + } + // Get free segment + const uint64_t free_segments = mm_allocator_get_num_segments_free(mm_allocator); + if (free_segments > 0) { + mm_allocator_segment_t* const segment = + mm_allocator_get_segment_free(mm_allocator,free_segments-1); + vector_dec_used(mm_allocator->segments_free); + mm_allocator->current_segment_idx = segment->idx; + return segment; + } + // Allocate new segment + mm_allocator_segment_t* const segment = mm_allocator_segment_new(mm_allocator); + mm_allocator->current_segment_idx = segment->idx; + return segment; +} +void* mm_allocator_allocate( + mm_allocator_t* const mm_allocator, + const uint64_t num_bytes, + const bool zero_mem, + const uint64_t align_bytes +#ifdef MM_ALLOCATOR_LOG + ,const char* func_name, + uint64_t line_no +#endif + ) { +#ifdef MM_ALLOCATOR_DISABLE + void* memory = calloc(1,num_bytes); + if (zero_mem) memset(memory,0,num_bytes); // Set zero + return memory; // TODO: alignment +#else + // Zero check + if (num_bytes == 0) { + fprintf(stderr,"MMAllocator error. Zero bytes requested\n"); + exit(1); + } + // Add payload + const uint64_t num_bytes_allocated = num_bytes + sizeof(mm_allocator_reference_t) + align_bytes; + // Fetch segment +#ifdef MM_ALLOCATOR_FORCE_MALLOC + mm_allocator_segment_t* const segment = NULL; // Force malloc memory +#else + mm_allocator_segment_t* const segment = mm_allocator_fetch_segment(mm_allocator,num_bytes_allocated); +#endif + if (segment != NULL) { + // Allocate memory + void* const memory_base = segment->memory + segment->used; + if (zero_mem) memset(memory_base,0,num_bytes_allocated); // Set zero + // Compute aligned memory + void* memory_aligned = memory_base + sizeof(mm_allocator_reference_t) + align_bytes; + if (align_bytes > 0) { + memory_aligned = memory_aligned - ((uintptr_t)memory_aligned % align_bytes); + } + // Set mm_reference + mm_allocator_reference_t* const mm_reference = (mm_allocator_reference_t*)(memory_aligned - sizeof(mm_allocator_reference_t)); + mm_reference->segment_idx = segment->idx; + mm_reference->request_idx = mm_allocator_segment_get_num_requests(segment); + // Add request + mm_allocator_request_t* request; + vector_alloc_new(segment->requests,mm_allocator_request_t,request); + request->offset = segment->used; + request->size = num_bytes_allocated; +#ifdef MM_ALLOCATOR_LOG + request->timestamp = (mm_allocator->request_ticker)++; + request->func_name = (char*)func_name; + request->line_no = line_no; +#endif + // Update segment + segment->used += num_bytes_allocated; + // Return memory + return memory_aligned; + } else { + // Malloc memory + void* const memory_base = malloc(num_bytes_allocated); + if (zero_mem) memset(memory_base,0,num_bytes_allocated); // Set zero + // Compute aligned memory + void* memory_aligned = memory_base + sizeof(mm_allocator_reference_t) + align_bytes; + if (align_bytes > 0) { + memory_aligned = memory_aligned - ((uintptr_t)memory_aligned % align_bytes); + } + // Set reference + mm_allocator_reference_t* const mm_reference = (mm_allocator_reference_t*)(memory_aligned - sizeof(mm_allocator_reference_t)); + mm_reference->segment_idx = UINT32_MAX; + mm_reference->request_idx = vector_get_used(mm_allocator->malloc_requests); + // Add malloc-request + mm_malloc_request_t* request; + vector_alloc_new(mm_allocator->malloc_requests,mm_malloc_request_t,request); + request->mem = memory_base; + request->size = num_bytes_allocated; +#ifdef MM_ALLOCATOR_LOG + request->timestamp = (mm_allocator->request_ticker)++; + request->func_name = (char*)func_name; + request->line_no = line_no; +#endif + request->reference = mm_reference; + // Return memory + return memory_aligned; + } +#endif +} +/* + * Allocator Free + */ +void mm_allocator_free_malloc_request( + mm_allocator_t* const mm_allocator, + mm_allocator_reference_t* const mm_reference) { + // Fetch request + mm_malloc_request_t* const request = + vector_get_elm(mm_allocator->malloc_requests,mm_reference->request_idx,mm_malloc_request_t); + // Check double-free + if (request->size == 0) { + fprintf(stderr,"MMAllocator error: double free\n"); + exit(1); + } + // Free request + request->size = 0; + free(request->mem); + ++(mm_allocator->malloc_requests_freed); + // Check number of freed requests + if (mm_allocator->malloc_requests_freed >= 1000) { + // Remove freed requests + const uint64_t num_requests = vector_get_used(mm_allocator->malloc_requests); + mm_malloc_request_t* const requests = vector_get_mem(mm_allocator->malloc_requests,mm_malloc_request_t); + uint64_t i, busy_requests = 0; + for (i=0;i 0) { + requests[busy_requests] = requests[i]; + requests[busy_requests].reference->request_idx = busy_requests; + ++busy_requests; + } + } + vector_set_used(mm_allocator->malloc_requests,busy_requests); + mm_allocator->malloc_requests_freed = 0; + } +} +void mm_allocator_free_allocator_request( + mm_allocator_t* const mm_allocator, + mm_allocator_reference_t* const mm_reference) { + // Fetch segment and request + mm_allocator_segment_t* const segment = + mm_allocator_get_segment(mm_allocator,mm_reference->segment_idx); + mm_allocator_request_t* const request = + mm_allocator_segment_get_request(segment,mm_reference->request_idx); + // Check double-free + if (MM_ALLOCATOR_REQUEST_IS_FREE(request)) { + fprintf(stderr,"MMAllocator error: double free\n"); + exit(1); + } + // Free request + MM_ALLOCATOR_REQUEST_SET_FREE(request); + // Free contiguous request(s) at the end of the segment + uint64_t num_requests = mm_allocator_segment_get_num_requests(segment); + if (mm_reference->request_idx == num_requests-1) { // Is the last request? + --num_requests; + mm_allocator_request_t* request = + vector_get_mem(segment->requests,mm_allocator_request_t) + (num_requests-1); + while (num_requests>0 && MM_ALLOCATOR_REQUEST_IS_FREE(request)) { + --num_requests; // Free request + --request; + } + // Update segment used + if (num_requests > 0) { + segment->used = request->offset + request->size; + vector_set_used(segment->requests,num_requests); + } else { + // Segment fully freed + mm_allocator_segment_clear(segment); // Clear + // Add to free segments (if it is not the current segment) + if (segment->idx != mm_allocator->current_segment_idx) { + vector_insert(mm_allocator->segments_free,segment,mm_allocator_segment_t*); + } + } + } +} +void mm_allocator_free( + mm_allocator_t* const mm_allocator, + void* const memory) { +#ifdef MM_ALLOCATOR_DISABLE + free(memory); +#else + // Get reference + void* const effective_memory = memory - sizeof(mm_allocator_reference_t); + mm_allocator_reference_t* const mm_reference = (mm_allocator_reference_t*) effective_memory; + if (mm_reference->segment_idx == UINT32_MAX) { + // Free malloc memory + mm_allocator_free_malloc_request(mm_allocator,mm_reference); + } else { + // Free allocator memory + mm_allocator_free_allocator_request(mm_allocator,mm_reference); + } +#endif +} +/* + * Utils + */ +void mm_allocator_get_occupation( + mm_allocator_t* const mm_allocator, + uint64_t* const bytes_used_malloc, + uint64_t* const bytes_used_allocator, + uint64_t* const bytes_free_available, + uint64_t* const bytes_free_fragmented) { + // Init + *bytes_used_malloc = 0; + *bytes_used_allocator = 0; + *bytes_free_available = 0; + *bytes_free_fragmented = 0; + // Check allocator memory + const uint64_t num_segments = mm_allocator_get_num_segments(mm_allocator); + int64_t segment_idx, request_idx; + for (segment_idx=0;segment_idx=0;--request_idx) { + mm_allocator_request_t* const request = mm_allocator_segment_get_request(segment,request_idx); + const uint64_t size = MM_ALLOCATOR_REQUEST_SIZE(request); + if (MM_ALLOCATOR_REQUEST_IS_FREE(request)) { + if (memory_freed) { + *bytes_free_available += size; + } else { + *bytes_free_fragmented += size; + } + } else { + memory_freed = false; + *bytes_used_allocator += size; + } + } + // Account for free space at the end of the segment + if (num_requests > 0) { + mm_allocator_request_t* const request = mm_allocator_segment_get_request(segment,num_requests-1); + const uint64_t bytes_free_at_end = segment->size - (request->offset+request->size); + if (segment_idx == mm_allocator->current_segment_idx) { + *bytes_free_available += bytes_free_at_end; + } else { + *bytes_free_fragmented += bytes_free_at_end; + } + } + } + // Check malloc memory + const uint64_t num_requests = vector_get_used(mm_allocator->malloc_requests); + mm_malloc_request_t* const requests = vector_get_mem(mm_allocator->malloc_requests,mm_malloc_request_t); + uint64_t i; + for (i=0;ioffset, + (uint64_t)MM_ALLOCATOR_REQUEST_SIZE(request) +#ifdef MM_ALLOCATOR_LOG + ,request->func_name, + request->line_no, + request->timestamp +#endif + ); +} +void mm_allocator_print_malloc_request( + FILE* const stream, + mm_malloc_request_t* const request) { + fprintf(stream," [@%p" PRIu64 "\t(%" PRIu64 " Bytes)" +#ifdef MM_ALLOCATOR_LOG + "\t%s:%" PRIu64 "\t{ts=%" PRIu64 "}" +#endif + "\n", + request->mem, + request->size +#ifdef MM_ALLOCATOR_LOG + ,request->func_name, + request->line_no, + request->timestamp +#endif + ); +} +void mm_allocator_print_allocator_requests( + FILE* const stream, + mm_allocator_t* const mm_allocator, + const bool compact_free) { + // Print allocator memory + uint64_t segment_idx, request_idx; + uint64_t free_block = 0; + bool has_requests = false; + fprintf(stream," => MMAllocator.requests\n"); + const uint64_t num_segments = mm_allocator_get_num_segments(mm_allocator); + for (segment_idx=0;segment_idx 0) { + fprintf(stream," [n/a\tFree] \t(%" PRIu64 " Bytes)\n",free_block); + free_block = 0; + } + mm_allocator_print_allocator_request(stream,request,segment_idx,request_idx); + has_requests = true; + } + } else { + mm_allocator_print_allocator_request(stream,request,segment_idx,request_idx); + has_requests = true; + } + } + } + if (!has_requests) { + fprintf(stream," -- No requests --\n"); + } + // Print malloc memory + fprintf(stream," => MMMalloc.requests\n"); + const uint64_t num_requests = vector_get_used(mm_allocator->malloc_requests); + mm_malloc_request_t* const requests = vector_get_mem(mm_allocator->malloc_requests,mm_malloc_request_t); + uint64_t i; + for (i=0;i 0) { + mm_allocator_print_malloc_request(stream,requests+i); + } + } + if (num_requests == 0) { + fprintf(stream," -- No requests --\n"); + } +} +void mm_allocator_print( + FILE* const stream, + mm_allocator_t* const mm_allocator, + const bool display_requests) { + // Print header + fprintf(stream,"MMAllocator.report\n"); + // Print segment information + const uint64_t num_segments = mm_allocator_get_num_segments(mm_allocator); + const uint64_t segment_size = mm_allocator->segment_size; + fprintf(stream," => Segments.allocated %" PRIu64 "\n",num_segments); + fprintf(stream," => Segments.size %" PRIu64 " MB\n",segment_size/(1024*1024)); + fprintf(stream," => Memory.available %" PRIu64 " MB\n",num_segments*(segment_size/(1024*1024))); + // Print memory information + uint64_t bytes_used_malloc, bytes_used_allocator; + uint64_t bytes_free_available, bytes_free_fragmented; + mm_allocator_get_occupation(mm_allocator,&bytes_used_malloc,&bytes_used_allocator,&bytes_free_available,&bytes_free_fragmented); + const float bytes_total = num_segments * segment_size; + const uint64_t bytes_free = bytes_free_available + bytes_free_fragmented; + fprintf(stream," => Memory.used %" PRIu64 " (%2.1f %%)\n", + bytes_used_allocator,100.0f*(float)bytes_used_allocator/bytes_total); + fprintf(stream," => Memory.free %" PRIu64 " (%2.1f %%)\n", + bytes_free,100.0f*(float)bytes_free/bytes_total); + fprintf(stream," => Memory.free.available %" PRIu64 " (%2.1f %%)\n", + bytes_free_available,100.0f*(float)bytes_free_available/bytes_total); + fprintf(stream," => Memory.free.fragmented %" PRIu64 " (%2.1f %%)\n", + bytes_free_fragmented,100.0f*(float)bytes_free_fragmented/bytes_total); + fprintf(stream," => Memory.malloc %" PRIu64 "\n",bytes_used_malloc); + // Print memory requests + if (display_requests) { + mm_allocator_print_allocator_requests(stream,mm_allocator,false); + } +} + + + diff --git a/src/lib/wfa2/system/mm_allocator.h b/src/lib/wfa2/system/mm_allocator.h new file mode 100644 index 000000000..381a20f35 --- /dev/null +++ b/src/lib/wfa2/system/mm_allocator.h @@ -0,0 +1,132 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * VERSION: v21.02.15 + * DESCRIPTION: Simple managed-memory allocator that reduces the overhead + * of using malloc/calloc/free functions by allocating slabs of memory + * and dispatching memory segments in order. + */ + +#ifndef MM_ALLOCATOR_H_ +#define MM_ALLOCATOR_H_ + +#include "../utils/vector.h" + +/* + * Configuration + */ +//#define MM_ALLOCATOR_LOG +#define MM_ALLOCATOR_ALIGNMENT 8 // 64bits + +/* + * MM-Allocator + */ +typedef struct { + // Metadata + uint64_t request_ticker; // Request ticker + // Memory segments + uint64_t segment_size; // Memory segment size (bytes) + vector_t* segments; // Memory segments (mm_allocator_segment_t*) + vector_t* segments_free; // Completely free segments (mm_allocator_segment_t*) + uint64_t current_segment_idx; // Current segment being used (serving memory) + // Malloc memory + vector_t* malloc_requests; // Malloc requests (mm_malloc_request_t) + uint64_t malloc_requests_freed; // Total malloc request freed and still in vector +} mm_allocator_t; + +/* + * Setup + */ +mm_allocator_t* mm_allocator_new( + const uint64_t segment_size); +void mm_allocator_clear( + mm_allocator_t* const mm_allocator); +void mm_allocator_delete( + mm_allocator_t* const mm_allocator); + +/* + * Allocator + */ +void* mm_allocator_allocate( + mm_allocator_t* const mm_allocator, + const uint64_t num_bytes, + const bool zero_mem, + const uint64_t align_bytes +#ifdef MM_ALLOCATOR_LOG + ,const char* func_name, + uint64_t line_no +#endif + ); + +#ifdef MM_ALLOCATOR_LOG +#define mm_allocator_alloc(mm_allocator,type) \ + ((type*)mm_allocator_allocate(mm_allocator,sizeof(type),false,MM_ALLOCATOR_ALIGNMENT,__func__,(uint64_t)__LINE__)) +#define mm_allocator_malloc(mm_allocator,num_bytes) \ + (mm_allocator_allocate(mm_allocator,num_bytes,false,MM_ALLOCATOR_ALIGNMENT,__func__,(uint64_t)__LINE__)) +#define mm_allocator_calloc(mm_allocator,num_elements,type,clear_mem) \ + ((type*)mm_allocator_allocate(mm_allocator,(num_elements)*sizeof(type),clear_mem,MM_ALLOCATOR_ALIGNMENT,__func__,(uint64_t)__LINE__)) +#else +#define mm_allocator_alloc(mm_allocator,type) \ + ((type*)mm_allocator_allocate(mm_allocator,sizeof(type),false,MM_ALLOCATOR_ALIGNMENT)) +#define mm_allocator_malloc(mm_allocator,num_bytes) \ + (mm_allocator_allocate(mm_allocator,num_bytes,false,MM_ALLOCATOR_ALIGNMENT)) +#define mm_allocator_calloc(mm_allocator,num_elements,type,clear_mem) \ + ((type*)mm_allocator_allocate(mm_allocator,(num_elements)*sizeof(type),clear_mem,MM_ALLOCATOR_ALIGNMENT)) +#endif + +#define mm_allocator_uint64(mm_allocator) mm_allocator_malloc(mm_allocator,sizeof(uint64_t)) +#define mm_allocator_uint32(mm_allocator) mm_allocator_malloc(mm_allocator,sizeof(uint32_t)) +#define mm_allocator_uint16(mm_allocator) mm_allocator_malloc(mm_allocator,sizeof(uint16_t)) +#define mm_allocator_uint8(mm_allocator) mm_allocator_malloc(mm_allocator,sizeof(uint8_t)) + +/* + * Free + */ +void mm_allocator_free( + mm_allocator_t* const mm_allocator, + void* const memory); + +/* + * Utils + */ +void mm_allocator_get_occupation( + mm_allocator_t* const mm_allocator, + uint64_t* const bytes_used_malloc, + uint64_t* const bytes_used_allocator, + uint64_t* const bytes_free_available, + uint64_t* const bytes_free_fragmented); + +/* + * Display + */ +void mm_allocator_print( + FILE* const stream, + mm_allocator_t* const mm_allocator, + const bool display_requests); + +#endif /* MM_ALLOCATOR_H_ */ diff --git a/src/lib/wfa2/system/mm_stack.c b/src/lib/wfa2/system/mm_stack.c new file mode 100644 index 000000000..eb57a5f36 --- /dev/null +++ b/src/lib/wfa2/system/mm_stack.c @@ -0,0 +1,269 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * VERSION: v21.02.15 + * DESCRIPTION: Simple managed-memory stack that reduces memory allocation + * overheads. Serves memory from large memory segments and frees all memory + * requested at once. + */ + +#include "mm_stack.h" + +/* + * Debug + */ +//#define MM_STACK_FORCE_MALLOC + +/* + * Constants + */ +#define MM_STACK_INITIAL_SEGMENTS 10 +#define MM_STACK_INITIAL_MALLOC_REQUESTS 10 +#define MM_STACK_INITIAL_STATES 10 + +/* + * Stack state + */ +typedef struct { + uint64_t segment_idx; + uint64_t segment_used; + uint64_t num_malloc_requests; +} mm_stack_state_t; + +/* + * Memory Segments + */ +typedef struct { + uint64_t size; // Total memory available + void* memory; // Memory + uint64_t used; // Bytes used (offset to memory next free byte) +} mm_stack_segment_t; + +/* + * Segments + */ +mm_stack_segment_t* mm_stack_segment_new( + mm_stack_t* const mm_stack) { + // Allocate handler + mm_stack_segment_t* const segment = (mm_stack_segment_t*) malloc(sizeof(mm_stack_segment_t)); + // Memory + segment->size = mm_stack->segment_size; + segment->memory = malloc(mm_stack->segment_size); + segment->used = 0; + // Add to segments + vector_insert(mm_stack->segments,segment,mm_stack_segment_t*); + // Return + return segment; +} +void mm_stack_segment_clear( + mm_stack_segment_t* const segment) { + segment->used = 0; +} +void mm_stack_segment_delete( + mm_stack_segment_t* const segment) { + free(segment->memory); + free(segment); +} +/* + * Setup + */ +mm_stack_t* mm_stack_new( + const uint64_t segment_size) { + // Allocate handler + mm_stack_t* const mm_stack = (mm_stack_t*) malloc(sizeof(mm_stack_t)); + // Memory segments + mm_stack->segments = vector_new(MM_STACK_INITIAL_SEGMENTS,mm_stack_segment_t*); + mm_stack->segment_size = segment_size; +#ifndef MM_STACK_FORCE_MALLOC + mm_stack_segment_new(mm_stack); +#endif + mm_stack->current_segment_idx = 0; + // Malloc memory + mm_stack->malloc_requests = vector_new(MM_STACK_INITIAL_MALLOC_REQUESTS,void*); + // Stack states + mm_stack->states = vector_new(MM_STACK_INITIAL_STATES,mm_stack_state_t); + // Return + return mm_stack; +} +void mm_stack_clear( + mm_stack_t* const mm_stack) { + // Clear first memory segment and discard the rest + mm_stack_segment_t* const segment = *vector_get_elm(mm_stack->segments,0,mm_stack_segment_t*); + mm_stack_segment_clear(segment); + mm_stack->current_segment_idx = 0; + // Free malloc memory + VECTOR_ITERATE(mm_stack->malloc_requests,mem_ptr,m,void*) { + free(*mem_ptr); + } + vector_clear(mm_stack->malloc_requests); + // Clear states + vector_clear(mm_stack->states); +} +void mm_stack_delete( + mm_stack_t* const mm_stack) { + // Delete memory segments + VECTOR_ITERATE(mm_stack->segments,segment_ptr,p,mm_stack_segment_t*) { + mm_stack_segment_delete(*segment_ptr); + } + vector_delete(mm_stack->segments); + // Free malloc memory + VECTOR_ITERATE(mm_stack->malloc_requests,mem_ptr,m,void*) { + free(*mem_ptr); + } + vector_delete(mm_stack->malloc_requests); + // Clear states + vector_delete(mm_stack->states); + // Free handler + free(mm_stack); +} +/* + * Allocator + */ +mm_stack_segment_t* mm_stack_fetch_segment( + mm_stack_t* const mm_stack, + const uint64_t num_bytes) { + // Fetch current segment + mm_stack_segment_t* const curr_segment = + *vector_get_elm(mm_stack->segments,mm_stack->current_segment_idx,mm_stack_segment_t*); +// // Check overall segment size +// if (num_bytes > curr_segment->size/2) { // Never buy anything you cannot afford twice +// return NULL; // Memory request over max-request size +// } + // Check available segment size + if (curr_segment->used + num_bytes <= curr_segment->size) { + return curr_segment; + } + // Check overall segment size + if (num_bytes > curr_segment->size) { + return NULL; // Memory request over segment size + } + // Get free segment + const uint64_t num_segments = vector_get_used(mm_stack->segments); + ++(mm_stack->current_segment_idx); + if (mm_stack->current_segment_idx < num_segments) { + // Get next segment + mm_stack_segment_t* const segment = + *vector_get_elm(mm_stack->segments,mm_stack->current_segment_idx,mm_stack_segment_t*); + // Clear + mm_stack_segment_clear(segment); + // Return + return segment; + } + // Add new segment + return mm_stack_segment_new(mm_stack); +} +void* mm_stack_allocate( + mm_stack_t* const mm_stack, + const uint64_t num_bytes, + const bool zero_mem, + const uint64_t align_bytes) { + // Zero check + if (num_bytes == 0) { + fprintf(stderr,"MMStack error. Zero bytes requested\n"); + exit(1); + } + // Add payload + const uint64_t num_bytes_allocated = num_bytes + align_bytes; + // Fetch segment +#ifdef MM_STACK_FORCE_MALLOC + mm_stack_segment_t* const segment = NULL; // Force malloc memory +#else + mm_stack_segment_t* const segment = mm_stack_fetch_segment(mm_stack,num_bytes_allocated); +#endif + // Allocate memory + void* memory_base ; + if (segment != NULL) { + // Segment-memory + memory_base = segment->memory + segment->used; + if (zero_mem) memset(memory_base,0,num_bytes_allocated); // Set zero + segment->used += num_bytes_allocated; // Update segment + } else { + // Malloc-memory + memory_base = malloc(num_bytes_allocated); + if (zero_mem) memset(memory_base,0,num_bytes_allocated); // Set zero + // Add malloc-request + vector_insert(mm_stack->malloc_requests,memory_base,void*); + } + // Check alignment + if (align_bytes == 0) return memory_base; + // Align memory request + void* memory_aligned = memory_base + align_bytes; + memory_aligned = memory_aligned - ((uintptr_t)memory_aligned % align_bytes); + return memory_aligned; +} +/* + * Push/pop states + */ +void mm_stack_push( + mm_stack_t* const mm_stack) { + // Get new stack-state + mm_stack_state_t* stack_state; + vector_alloc_new(mm_stack->states,mm_stack_state_t,stack_state); + // Store current state + mm_stack_segment_t* const current_segment = + *vector_get_elm(mm_stack->segments,mm_stack->current_segment_idx,mm_stack_segment_t*); + stack_state->segment_idx = mm_stack->current_segment_idx; + stack_state->segment_used = current_segment->used; + stack_state->num_malloc_requests = vector_get_used(mm_stack->malloc_requests); +} +void mm_stack_pop( + mm_stack_t* const mm_stack) { + // Get last stack-state + mm_stack_state_t* const stack_state = vector_get_last_elm(mm_stack->states,mm_stack_state_t); + vector_dec_used(mm_stack->states); + // Restore segment-memory state + mm_stack->current_segment_idx = stack_state->segment_idx; + mm_stack_segment_t* const current_segment = + *(vector_get_elm(mm_stack->segments,stack_state->segment_idx,mm_stack_segment_t*)); + current_segment->used = stack_state->segment_used; + // Restore malloc-memory state (free requests) + const uint64_t total_malloc_requests = vector_get_used(mm_stack->malloc_requests); + void** const malloc_requests = vector_get_mem(mm_stack->malloc_requests,void*); + uint64_t i; + for (i=stack_state->num_malloc_requests;imalloc_requests,stack_state->num_malloc_requests); +} +/* + * Display + */ +void mm_stack_print( + FILE* const stream, + mm_stack_t* const mm_stack) { + // Print header + fprintf(stream,"MMStack.report\n"); + // Print segment information + const uint64_t num_segments = vector_get_used(mm_stack->segments); + const uint64_t segment_size = mm_stack->segment_size; + fprintf(stream," => Segments.allocated %" PRIu64 "\n",num_segments); + fprintf(stream," => Segments.size %" PRIu64 " MB\n",segment_size/(1024*1024)); + fprintf(stream," => Memory.available %" PRIu64 " MB\n",num_segments*(segment_size/(1024*1024))); +} + + diff --git a/src/lib/wfa2/system/mm_stack.h b/src/lib/wfa2/system/mm_stack.h new file mode 100644 index 000000000..1c6431f91 --- /dev/null +++ b/src/lib/wfa2/system/mm_stack.h @@ -0,0 +1,105 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * VERSION: v21.02.15 + * DESCRIPTION: Simple managed-memory stack that reduces memory allocation + * overheads. Serves memory from large memory segments and frees all memory + * requested at once. + */ + +#ifndef MM_STACK_H_ +#define MM_STACK_H_ + +#include "../utils/vector.h" + +/* + * Configuration + */ +#define MM_STACK_ALIGNMENT 8 // 64bits + +/* + * MM-Allocator + */ +typedef struct { + // Memory segments + uint64_t segment_size; // Memory segment size (bytes) + vector_t* segments; // Memory segments (mm_stack_segment_t*) + uint64_t current_segment_idx; // Current segment being used (serving memory) + // Malloc memory + vector_t* malloc_requests; // Malloc requests (void*) + // Stack states + vector_t* states; // Stack saved states (mm_stack_state_t) +} mm_stack_t; + +/* + * Setup + */ +mm_stack_t* mm_stack_new( + const uint64_t segment_size); +void mm_stack_clear( + mm_stack_t* const mm_stack); +void mm_stack_delete( + mm_stack_t* const mm_stack); + +/* + * Allocator + */ +void* mm_stack_allocate( + mm_stack_t* const mm_stack, + const uint64_t num_bytes, + const bool zero_mem, + const uint64_t align_bytes); + +#define mm_stack_alloc(mm_stack,type) \ + ((type*)mm_stack_allocate(mm_stack,sizeof(type),false,MM_STACK_ALIGNMENT)) +#define mm_stack_malloc(mm_stack,num_bytes) \ + (mm_stack_allocate(mm_stack,num_bytes,false,MM_STACK_ALIGNMENT)) +#define mm_stack_calloc(mm_stack,num_elements,type,clear_mem) \ + ((type*)mm_stack_allocate(mm_stack,(num_elements)*sizeof(type),clear_mem,MM_STACK_ALIGNMENT)) + +#define mm_stack_uint64(mm_stack) mm_stack_malloc(mm_stack,sizeof(uint64_t)) +#define mm_stack_uint32(mm_stack) mm_stack_malloc(mm_stack,sizeof(uint32_t)) +#define mm_stack_uint16(mm_stack) mm_stack_malloc(mm_stack,sizeof(uint16_t)) +#define mm_stack_uint8(mm_stack) mm_stack_malloc(mm_stack,sizeof(uint8_t)) + +/* + * Push/pop states + */ +void mm_stack_push( + mm_stack_t* const mm_stack); +void mm_stack_pop( + mm_stack_t* const mm_stack); + +/* + * Display + */ +void mm_stack_print( + FILE* const stream, + mm_stack_t* const mm_stack); + +#endif /* MM_STACK_H_ */ diff --git a/src/lib/wfa2/system/profiler_counter.c b/src/lib/wfa2/system/profiler_counter.c new file mode 100644 index 000000000..465d3167f --- /dev/null +++ b/src/lib/wfa2/system/profiler_counter.c @@ -0,0 +1,293 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * VERSION: v20.08.25 + * DESCRIPTION: Simple profile counter + */ + +#include "profiler_counter.h" + +/* + * Counters + */ +void counter_reset( + profiler_counter_t* const counter) { + memset(counter,0,sizeof(profiler_counter_t)); +} +void counter_add( + profiler_counter_t* const counter, + const uint64_t amount) { + // Add to total & increment number of samples + counter->total += amount; + ++(counter->samples); + // From http://www.johndcook.com/standard_deviation.html + // See Knuth TAOCP vol 2, 3rd edition, page 232 + if (counter->samples == 1) { + counter->min = amount; + counter->max = amount; + counter->m_oldM = amount; + counter->m_newM = amount; + counter->m_oldS = 0.0; + } else { + counter->min = MIN(counter->min,amount); + counter->max = MAX(counter->max,amount); + counter->m_newM = counter->m_oldM + ((double)amount-counter->m_oldM)/(double)counter->samples; + counter->m_newS = counter->m_oldS + ((double)amount-counter->m_oldM)*((double)amount-counter->m_newM); + counter->m_oldM = counter->m_newM; + counter->m_oldS = counter->m_newS; + } +} +uint64_t counter_get_total(const profiler_counter_t* const counter) { + return counter->total; +} +uint64_t counter_get_num_samples(const profiler_counter_t* const counter) { + return counter->samples; +} +uint64_t counter_get_min(const profiler_counter_t* const counter) { + return counter->min; +} +uint64_t counter_get_max(const profiler_counter_t* const counter) { + return counter->max; +} +double counter_get_mean(const profiler_counter_t* const counter) { + return (double)counter->total/(double)counter->samples; +} +double counter_get_variance(const profiler_counter_t* const counter) { + return ((counter->samples > 1) ? counter->m_newS/(double)(counter->samples - 1) : 0.0); +} +double counter_get_stddev(const profiler_counter_t* const counter) { + return sqrt(counter_get_variance(counter)); +} +void counter_combine_sum( + profiler_counter_t* const counter_dst, + profiler_counter_t* const counter_src) { + counter_dst->total += counter_src->total; + counter_dst->samples += counter_src->samples; + counter_dst->min = MIN(counter_dst->min,counter_src->min); + counter_dst->max = MAX(counter_dst->max,counter_src->max); + if (counter_src->m_newS!=0.0) counter_dst->m_newS = counter_src->m_newS; + if (counter_src->m_newM!=0.0) counter_dst->m_newM = counter_src->m_newM; + if (counter_src->m_oldS!=0.0) counter_dst->m_oldS = counter_src->m_oldS; + if (counter_src->m_oldM!=0.0) counter_dst->m_oldM = counter_src->m_oldM; +} +void counter_print_stats( + FILE* const stream, + const profiler_counter_t* const counter, + const profiler_counter_t* const ref_counter, + const char* const units) { + // Print Samples + const uint64_t num_samples = counter_get_num_samples(counter); + if (num_samples >= METRIC_FACTOR_1G) { + fprintf(stream," (samples=%" PRIu64 "G",num_samples/METRIC_FACTOR_1G); + } else if (num_samples >= METRIC_FACTOR_1M) { + fprintf(stream," (samples=%" PRIu64 "M",num_samples/METRIC_FACTOR_1M); + } else if (num_samples >= METRIC_FACTOR_1K) { + fprintf(stream," (samples=%" PRIu64 "K",num_samples/METRIC_FACTOR_1K); + } else { + fprintf(stream," (samples=%" PRIu64 "",num_samples); + if (num_samples==0) { + fprintf(stream,",--n/a--)}\n"); + return; + } + } + // Print Mean + const double mean = counter_get_mean(counter); + if (mean >= METRIC_FACTOR_1G) { + fprintf(stream,"{mean%.2fG",mean/METRIC_FACTOR_1G); + } else if (mean >= METRIC_FACTOR_1M) { + fprintf(stream,"{mean%.2fM",mean/METRIC_FACTOR_1M); + } else if (mean >= METRIC_FACTOR_1K) { + fprintf(stream,"{mean%.2fK",mean/METRIC_FACTOR_1K); + } else { + fprintf(stream,"{mean%.2f",mean); + } + // Print Min + const uint64_t min = counter_get_min(counter); + if (min >= METRIC_FACTOR_1G) { + fprintf(stream,",min%.2fG",(double)min/METRIC_FACTOR_1G); + } else if (min >= METRIC_FACTOR_1M) { + fprintf(stream,",min%.2fM",(double)min/METRIC_FACTOR_1M); + } else if (min >= METRIC_FACTOR_1K) { + fprintf(stream,",min%.2fK",(double)min/METRIC_FACTOR_1K); + } else { + fprintf(stream,",min%.2f",(double)min); + } + // Print Max + const uint64_t max = counter_get_max(counter); + if (max >= METRIC_FACTOR_1G) { + fprintf(stream,",Max%.2fG",(double)max/METRIC_FACTOR_1G); + } else if (max >= METRIC_FACTOR_1M) { + fprintf(stream,",Max%.2fM",(double)max/METRIC_FACTOR_1M); + } else if (max >= METRIC_FACTOR_1K) { + fprintf(stream,",Max%.2fK",(double)max/METRIC_FACTOR_1K); + } else { + fprintf(stream,",Max%.2f",(double)max); + } + // Print Variance + const uint64_t var = counter_get_variance(counter); + if (var >= METRIC_FACTOR_1G) { + fprintf(stream,",Var%.2fG",(double)var/METRIC_FACTOR_1G); + } else if (var >= METRIC_FACTOR_1M) { + fprintf(stream,",Var%.2fM",(double)var/METRIC_FACTOR_1M); + } else if (var >= METRIC_FACTOR_1K) { + fprintf(stream,",Var%.2fK",(double)var/METRIC_FACTOR_1K); + } else { + fprintf(stream,",Var%.2f",(double)var); + } + // Print Standard Deviation + const uint64_t stdDev = counter_get_stddev(counter); + if (stdDev >= METRIC_FACTOR_1G) { + fprintf(stream,",StdDev%.2fG)}\n",(double)stdDev/METRIC_FACTOR_1G); + } else if (stdDev >= METRIC_FACTOR_1M) { + fprintf(stream,",StdDev%.2fM)}\n",(double)stdDev/METRIC_FACTOR_1M); + } else if (stdDev >= METRIC_FACTOR_1K) { + fprintf(stream,",StdDev%.2fK)}\n",(double)stdDev/METRIC_FACTOR_1K); + } else { + fprintf(stream,",StdDev%.2f)}\n",(double)stdDev); + } +} +void counter_print( + FILE* const stream, + const profiler_counter_t* const counter, + const profiler_counter_t* const ref_counter, + const char* const units, + const bool full_report) { + const uint64_t total = counter_get_total(counter); + // Print Total + if (total >= METRIC_FACTOR_1G) { + fprintf(stream,"%7.2f G%s",(double)total/METRIC_FACTOR_1G,units); + } else if (total >= METRIC_FACTOR_1M) { + fprintf(stream,"%7.2f M%s",(double)total/METRIC_FACTOR_1M,units); + } else if (total >= METRIC_FACTOR_1K) { + fprintf(stream,"%7.2f K%s",(double)total/METRIC_FACTOR_1K,units); + } else { + fprintf(stream,"%7.2f %s ",(double)total,units); + } + // Print percentage wrt reference + if (ref_counter!=NULL) { + if (total==0) { + fprintf(stream," ( 0.00 %%)"); + } else { + const uint64_t total_ref = counter_get_total(ref_counter); + if (total_ref==0) { + fprintf(stream," ( n/a %%)"); + } else { + const double percentage = (double)(total*100)/(double)total_ref; + fprintf(stream," (%6.02f %%)",percentage); + } + } + } else { + fprintf(stream," "); + } + // Full report + if (!full_report) { + fprintf(stream,"\n"); + return; + } else { + counter_print_stats(stream,counter,ref_counter,units); + } +} +void percentage_print( + FILE* const stream, + const profiler_counter_t* const counter, + const char* const units) { + // Print Mean + const double mean = counter_get_mean(counter); + fprintf(stream,"%7.2f %%%s\t\t",mean,units); + // Print Samples + const uint64_t num_samples = counter_get_num_samples(counter); + if (num_samples >= METRIC_FACTOR_1G) { + fprintf(stream," (samples=%" PRIu64 "G",num_samples/METRIC_FACTOR_1G); + } else if (num_samples >= METRIC_FACTOR_1M) { + fprintf(stream," (samples=%" PRIu64 "M",num_samples/METRIC_FACTOR_1M); + } else if (num_samples >= METRIC_FACTOR_1K) { + fprintf(stream," (samples=%" PRIu64 "K",num_samples/METRIC_FACTOR_1K); + } else { + fprintf(stream," (samples=%" PRIu64 "",num_samples); + } + if (num_samples == 0) { + fprintf(stream,")\n"); + return; + } + // Print Min/Max + fprintf(stream,",min%.2f%%,Max%.2f%%", + (double)counter_get_min(counter),(double)counter_get_max(counter)); + // Print Variance/StandardDeviation + fprintf(stream,",Var%.2f,StdDev%.2f)\n", + counter_get_variance(counter),counter_get_stddev(counter)); +} +/* + * Reference Counter (Counts wrt a reference counter. Eg ranks) + */ +void rcounter_start( + profiler_rcounter_t* const rcounter, + const uint64_t reference) { + rcounter->accumulated = 0; + rcounter->begin_count = reference; +} +void rcounter_stop( + profiler_rcounter_t* const rcounter, + const uint64_t reference) { + rcounter_pause(rcounter,reference); + counter_add(&rcounter->counter,rcounter->accumulated); +} +void rcounter_pause( + profiler_rcounter_t* const rcounter, + const uint64_t reference) { + rcounter->accumulated += reference - rcounter->begin_count; +} +void rcounter_restart( + profiler_rcounter_t* const rcounter, + const uint64_t reference) { + rcounter->begin_count = reference; +} +void rcounter_reset( + profiler_rcounter_t* const rcounter) { + counter_reset(&rcounter->counter); +} +uint64_t rcounter_get_total(profiler_rcounter_t* const rcounter) { + return counter_get_total(&rcounter->counter); +} +uint64_t rcounter_get_num_samples(profiler_rcounter_t* const rcounter) { + return counter_get_num_samples(&rcounter->counter); +} +uint64_t rcounter_get_min(profiler_rcounter_t* const rcounter) { + return counter_get_min(&rcounter->counter); +} +uint64_t rcounter_get_max(profiler_rcounter_t* const rcounter) { + return counter_get_max(&rcounter->counter); +} +uint64_t rcounter_get_mean(profiler_rcounter_t* const rcounter) { + return counter_get_mean(&rcounter->counter); +} +uint64_t rcounter_get_variance(profiler_rcounter_t* const rcounter) { + return counter_get_variance(&rcounter->counter); +} +uint64_t rcounter_get_stddev(profiler_rcounter_t* const rcounter) { + return counter_get_stddev(&rcounter->counter); +} diff --git a/src/lib/wfa2/system/profiler_counter.h b/src/lib/wfa2/system/profiler_counter.h new file mode 100644 index 000000000..0bb7468a7 --- /dev/null +++ b/src/lib/wfa2/system/profiler_counter.h @@ -0,0 +1,133 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * VERSION: v20.08.25 + * DESCRIPTION: Simple profile counter + */ + +#ifndef PROFILER_COUNTER_H_ +#define PROFILER_COUNTER_H_ + +#include "../utils/commons.h" + +/* + * Counters + */ +typedef struct { + uint64_t total; + uint64_t samples; + uint64_t min; + uint64_t max; + double m_oldM; + double m_newM; + double m_oldS; + double m_newS; +} profiler_counter_t; + +void counter_reset( + profiler_counter_t* const counter); +void counter_add( + profiler_counter_t* const counter, + const uint64_t amount); + +uint64_t counter_get_total(const profiler_counter_t* const counter); +uint64_t counter_get_num_samples(const profiler_counter_t* const counter); +uint64_t counter_get_min(const profiler_counter_t* const counter); +uint64_t counter_get_max(const profiler_counter_t* const counter); +double counter_get_mean(const profiler_counter_t* const counter); +double counter_get_variance(const profiler_counter_t* const counter); +double counter_get_stddev(const profiler_counter_t* const counter); + +void counter_combine_sum( + profiler_counter_t* const counter_dst, + profiler_counter_t* const counter_src); + +void counter_print( + FILE* const stream, + const profiler_counter_t* const counter, + const profiler_counter_t* const ref_counter, + const char* const units, + const bool full_report); +void percentage_print( + FILE* const stream, + const profiler_counter_t* const counter, + const char* const units); + +/* + * Reference Counter (Counts wrt a reference counter. Eg ranks) + */ +typedef struct { + uint64_t begin_count; // Counter + profiler_counter_t counter; // Total count & samples taken + uint64_t accumulated; // Total accumulated +} profiler_rcounter_t; + +void rcounter_start( + profiler_rcounter_t* const rcounter, + const uint64_t reference); +void rcounter_stop( + profiler_rcounter_t* const rcounter, + const uint64_t reference); +void rcounter_pause( + profiler_rcounter_t* const rcounter, + const uint64_t reference); +void rcounter_restart( + profiler_rcounter_t* const rcounter, + const uint64_t reference); +void rcounter_reset( + profiler_rcounter_t* const rcounter); + +uint64_t rcounter_get_total(profiler_rcounter_t* const rcounter); +uint64_t rcounter_get_num_samples(profiler_rcounter_t* const rcounter); +uint64_t rcounter_get_min(profiler_rcounter_t* const rcounter); +uint64_t rcounter_get_max(profiler_rcounter_t* const rcounter); +uint64_t rcounter_get_mean(profiler_rcounter_t* const rcounter); +uint64_t rcounter_get_variance(profiler_rcounter_t* const rcounter); +uint64_t rcounter_get_stddev(profiler_rcounter_t* const rcounter); + +/* + * Display + */ +#define PRIcounter "lu(#%" PRIu64 ",m%" PRIu64 ",M%" PRIu64",{%.2f})" +#define PRIcounterVal(counter) \ + counter_get_total(counter), \ + counter_get_num_samples(counter), \ + counter_get_min(counter), \ + counter_get_max(counter), \ + counter_get_mean(counter) +#define PRIcounterX "lu(#%" PRIu64 ",m%" PRIu64 ",M%" PRIu64 ",{%.2f,%.2f,%.2f})" +#define PRIcounterXVal(counter) \ + counter_get_total(counter), \ + counter_get_num_samples(counter), \ + counter_get_min(counter), \ + counter_get_max(counter), \ + counter_get_mean(counter), \ + counter_get_variance(counter), \ + counter_get_stddev(counter) + +#endif /* PROFILER_COUNTER_H_ */ diff --git a/src/lib/wfa2/system/profiler_timer.c b/src/lib/wfa2/system/profiler_timer.c new file mode 100644 index 000000000..cec1fd9ef --- /dev/null +++ b/src/lib/wfa2/system/profiler_timer.c @@ -0,0 +1,199 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * VERSION: v20.10.14 + * DESCRIPTION: Simple time profiler + */ + +#include "profiler_timer.h" + +#ifdef __MACH__ +#include +#include +#include +#endif + +/* + * System timer + */ +void timer_get_system_time(struct timespec *ts) { +#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(),CALENDAR_CLOCK,&cclock); + clock_get_time(cclock,&mts); + mach_port_deallocate(mach_task_self(),cclock); + ts->tv_sec = mts.tv_sec; + ts->tv_nsec = mts.tv_nsec; +#else + clock_gettime(CLOCK_REALTIME,ts); +#endif +} +/* + * Timers + */ +void timer_start(profiler_timer_t* const timer) { + timer->accumulated = 0; + timer_continue(timer); +} +void timer_stop(profiler_timer_t* const timer) { + timer_pause(timer); + counter_add(&timer->time_ns,timer->accumulated); + timer->accumulated = 0; +} +void timer_pause(profiler_timer_t* const timer) { + struct timespec end_timer; + timer_get_system_time(&end_timer); + timer->accumulated += TIME_DIFF_NS(timer->begin_timer,end_timer); +} +void timer_continue(profiler_timer_t* const timer) { + timer_get_system_time(&timer->begin_timer); +} +void timer_reset(profiler_timer_t* const timer) { + timer->accumulated = 0; + counter_reset(&timer->time_ns); +} +uint64_t timer_get_current_lap_ns(profiler_timer_t* const timer) { + struct timespec end_timer; + timer_get_system_time(&end_timer); + return timer->accumulated + TIME_DIFF_NS(timer->begin_timer,end_timer); +} +uint64_t timer_get_current_total_ns(profiler_timer_t* const timer) { + return counter_get_total(&timer->time_ns) + timer_get_current_lap_ns(timer); +} +uint64_t timer_get_total_ns(const profiler_timer_t* const timer) { + return counter_get_total(&timer->time_ns) + timer->accumulated; +} +uint64_t timer_get_num_samples(const profiler_timer_t* const timer) { + return counter_get_num_samples(&timer->time_ns); +} +uint64_t timer_get_min_ns(const profiler_timer_t* const timer) { + return counter_get_min(&timer->time_ns); +} +uint64_t timer_get_max_ns(const profiler_timer_t* const timer) { + return counter_get_max(&timer->time_ns); +} +uint64_t timer_get_mean(const profiler_timer_t* const timer) { + return counter_get_mean(&timer->time_ns); +} +uint64_t timer_get_variance(const profiler_timer_t* const timer) { + return counter_get_variance(&timer->time_ns); +} +uint64_t timer_get_stddev(const profiler_timer_t* const timer) { + return counter_get_stddev(&timer->time_ns); +} +void timer_print_total( + FILE* const stream, + const profiler_timer_t* const timer) { + const uint64_t total_time_ns = timer_get_total_ns(timer); + // Print Total + if (total_time_ns >= 60000000000ull) { + fprintf(stream,"%7.2f m ",TIMER_CONVERT_NS_TO_M(total_time_ns)); + } else if (total_time_ns >= 1000000000) { + fprintf(stream,"%7.2f s ",TIMER_CONVERT_NS_TO_S(total_time_ns)); + } else if (total_time_ns >= 1000000) { + fprintf(stream,"%7.2f ms",TIMER_CONVERT_NS_TO_MS(total_time_ns)); + } else if (total_time_ns >= 1000) { + fprintf(stream,"%7.2f us",TIMER_CONVERT_NS_TO_US(total_time_ns)); + } else { + fprintf(stream,"%7" PRIu64 " ns",total_time_ns); + } +} +void timer_print( + FILE* const stream, + const profiler_timer_t* const timer, + const profiler_timer_t* const ref_timer) { + const uint64_t total_time_ns = timer_get_total_ns(timer); + // Print Total + timer_print_total(stream,timer); + // Print percentage wrt reference + if (ref_timer!=NULL) { + if (total_time_ns==0) { + fprintf(stream," ( 0.00 %%)"); + } else { + const uint64_t total_ref_time_ns = timer_get_total_ns(ref_timer); + if (total_ref_time_ns==0) { + fprintf(stream," ( n/a %%)"); + } else { + const double percentage = (double)(total_time_ns*100)/(double)total_ref_time_ns; + fprintf(stream," (%6.02f %%)",percentage); + } + } + } + // Print Calls + const uint64_t num_calls = timer_get_num_samples(timer); + if (num_calls >= 1000000000) { + fprintf(stream," (%5" PRIu64 " Gcalls",num_calls/1000000000); + } else if (num_calls >= 1000000) { + fprintf(stream," (%5" PRIu64 " Mcalls",num_calls/1000000); + } else if (num_calls >= 1000) { + fprintf(stream," (%5" PRIu64 " Kcalls",num_calls/1000); + } else if (num_calls > 1 || num_calls == 0) { + fprintf(stream," (%5" PRIu64 " calls",num_calls); + } else { + fprintf(stream," (%5" PRIu64 " call",num_calls); + } + // Print time/call + if (num_calls==0) { + fprintf(stream,", n/a s/call)\n"); + return; + } else { + const uint64_t ns_per_call = total_time_ns / num_calls; + if (ns_per_call > 1000000000) { + fprintf(stream,",%7.2f s/call",TIMER_CONVERT_NS_TO_S(ns_per_call)); + } else if (ns_per_call > 1000000) { + fprintf(stream,",%7.2f ms/call",TIMER_CONVERT_NS_TO_MS(ns_per_call)); + } else if (ns_per_call > 1000) { + fprintf(stream,",%7.2f us/call",TIMER_CONVERT_NS_TO_US(ns_per_call)); + } else { + fprintf(stream,",%7" PRIu64 " ns/call",ns_per_call); + } + } + // Print Max + const uint64_t min_ns = timer_get_min_ns(timer); + if (min_ns > 1000000000) { + fprintf(stream," {min%.2fs",TIMER_CONVERT_NS_TO_S(min_ns)); + } else if (min_ns > 1000000) { + fprintf(stream," {min%.2fms",TIMER_CONVERT_NS_TO_MS(min_ns)); + } else if (min_ns > 1000) { + fprintf(stream," {min%.2fus",TIMER_CONVERT_NS_TO_US(min_ns)); + } else { + fprintf(stream," {min%" PRIu64 "ns",min_ns); + } + // Print Min + const uint64_t max_ns = timer_get_max_ns(timer); + if (max_ns > 1000000000) { + fprintf(stream,",Max%.2fs})\n",TIMER_CONVERT_NS_TO_S(max_ns)); + } else if (max_ns > 1000000) { + fprintf(stream,",Max%.2fms})\n",TIMER_CONVERT_NS_TO_MS(max_ns)); + } else if (max_ns > 1000) { + fprintf(stream,",Max%.2fus})\n",TIMER_CONVERT_NS_TO_US(max_ns)); + } else { + fprintf(stream,",Max%" PRIu64 "ns})\n",max_ns); + } +} diff --git a/src/lib/wfa2/system/profiler_timer.h b/src/lib/wfa2/system/profiler_timer.h new file mode 100644 index 000000000..5d94e7ac6 --- /dev/null +++ b/src/lib/wfa2/system/profiler_timer.h @@ -0,0 +1,98 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * VERSION: v20.08.25 + * DESCRIPTION: Simple time profiler + */ + +#ifndef PROFILER_TIMER_H +#define PROFILER_TIMER_H + +#include "../utils/commons.h" +#include "profiler_counter.h" + +/* + * Time Conversions + */ +#define TIME_DIFF_NS(start,end) ((end.tv_sec*1000000000 + end.tv_nsec) - (start.tv_sec*1000000000 + start.tv_nsec)) +#define TIME_DIFF_S(start,end) ((end.tv_sec + end.tv_nsec/1E9) - (start.tv_sec + start.tv_nsec/1E9)) + +#define TIMER_CONVERT_NS_TO_US(time_ns) ((double)(time_ns)/1E3) +#define TIMER_CONVERT_NS_TO_MS(time_ns) ((double)(time_ns)/1E6) +#define TIMER_CONVERT_NS_TO_S(time_ns) ((double)(time_ns)/1E9) +#define TIMER_CONVERT_NS_TO_M(time_ns) ((double)(time_ns)/1E9/60.0) +#define TIMER_CONVERT_NS_TO_H(time_ns) ((double)(time_ns)/1E9/3600.0) + +/* + * System time + */ +void timer_get_system_time(struct timespec *ts); + +/* + * Timers + */ +typedef struct { + /* Timer */ + struct timespec begin_timer; // Timer begin + /* Total time & samples taken */ + profiler_counter_t time_ns; + uint64_t accumulated; +} profiler_timer_t; + +void timer_start(profiler_timer_t* const timer); +void timer_stop(profiler_timer_t* const timer); +void timer_pause(profiler_timer_t* const timer); +void timer_continue(profiler_timer_t* const timer); +void timer_reset(profiler_timer_t* const timer); + +uint64_t timer_get_current_lap_ns(profiler_timer_t* const timer); +uint64_t timer_get_current_total_ns(profiler_timer_t* const timer); +uint64_t timer_get_total_ns(const profiler_timer_t* const timer); +uint64_t timer_get_num_samples(const profiler_timer_t* const timer); +uint64_t timer_get_min_ns(const profiler_timer_t* const timer); +uint64_t timer_get_max_ns(const profiler_timer_t* const timer); +uint64_t timer_get_mean(const profiler_timer_t* const timer); +uint64_t timer_get_variance(const profiler_timer_t* const timer); +uint64_t timer_get_stddev(const profiler_timer_t* const timer); + +void timer_print_total( + FILE* const stream, + const profiler_timer_t* const timer); + +void timer_print( + FILE* const stream, + const profiler_timer_t* const timer, + const profiler_timer_t* const ref_timer); + +#define TIMER_GET_TOTAL_US(timer) TIMER_CONVERT_NS_TO_US(timer_get_total_ns(timer)) +#define TIMER_GET_TOTAL_MS(timer) TIMER_CONVERT_NS_TO_MS(timer_get_total_ns(timer)) +#define TIMER_GET_TOTAL_S(timer) TIMER_CONVERT_NS_TO_S(timer_get_total_ns(timer)) +#define TIMER_GET_TOTAL_M(timer) TIMER_CONVERT_NS_TO_M(timer_get_total_ns(timer)) +#define TIMER_GET_TOTAL_H(timer) TIMER_CONVERT_NS_TO_H(timer_get_total_ns(timer)) + +#endif /* PROFILER_TIMER_H */ diff --git a/src/lib/wfa2/utils/bitmap.c b/src/lib/wfa2/utils/bitmap.c new file mode 100644 index 000000000..485ba953b --- /dev/null +++ b/src/lib/wfa2/utils/bitmap.c @@ -0,0 +1,127 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Basic bitmap datastructure (static) + */ + +#include "../utils/bitmap.h" +#include "../system/mm_allocator.h" + +/* + * Setup + */ +bitmap_t* bitmap_new( + const uint64_t length, + mm_allocator_t* const mm_allocator) { + // Allocate + bitmap_t* const bitmap = + mm_allocator_alloc(mm_allocator,bitmap_t); + bitmap->mm_allocator = mm_allocator; + // Allocate bitmap-blocks + const uint64_t num_blocks = DIV_CEIL(length,BITMAP_BLOCK_ELEMENTS); + bitmap->num_blocks = num_blocks; + bitmap->bitmap_blocks = mm_allocator_calloc(mm_allocator,num_blocks,bitmap_block_t,true); + // Return + return bitmap; +} +void bitmap_delete( + bitmap_t* const bitmap) { + // Parameters + mm_allocator_t* const mm_allocator = bitmap->mm_allocator; + // Free + mm_allocator_free(mm_allocator,bitmap->bitmap_blocks); + mm_allocator_free(mm_allocator,bitmap); +} +/* + * Accessors + */ +void bitmap_set( + bitmap_t* const bitmap, + const uint64_t position) { + // Locate block + const uint64_t block_num = position / BITMAP_BLOCK_ELEMENTS; + const uint64_t block_pos = position % BITMAP_BLOCK_ELEMENTS; + // Set bitmap + bitmap->bitmap_blocks[block_num].bitmap |= (BITMAP_BLOCK_MASK << block_pos); +} +bool bitmap_is_set( + bitmap_t* const bitmap, + const uint64_t position) { + // Locate block + const uint64_t block_num = position / BITMAP_BLOCK_ELEMENTS; + const uint64_t block_pos = position % BITMAP_BLOCK_ELEMENTS; + // Set bitmap + return bitmap->bitmap_blocks[block_num].bitmap & (BITMAP_BLOCK_MASK << block_pos); +} + +bool bitmap_check__set( + bitmap_t* const bitmap, + const uint64_t position) { + // Locate block + const uint64_t block_num = position / BITMAP_BLOCK_ELEMENTS; + const uint64_t block_pos = position % BITMAP_BLOCK_ELEMENTS; + // Check bit set + if (bitmap->bitmap_blocks[block_num].bitmap & (BITMAP_BLOCK_MASK << block_pos)) { + return true; // Return true (it was set) + } else { + // Set bitmap + bitmap->bitmap_blocks[block_num].bitmap |= (BITMAP_BLOCK_MASK << block_pos); + return false; // Return false (it was not set) + } +} +/* + * Rank + */ +void bitmap_update_counters( + bitmap_t* const bitmap) { + // Parameters + const uint64_t num_blocks = bitmap->num_blocks; + bitmap_block_t* bitmap_block = bitmap->bitmap_blocks; + // Update all counters + uint64_t acc_count = 0; + uint64_t i; + for (i=0;icounter = acc_count; + acc_count += POPCOUNT_64(bitmap_block->bitmap); + } +} +uint64_t bitmap_erank( + bitmap_t* const bitmap, + const uint64_t position) { + // Locate block + const uint64_t block_num = position / BITMAP_BLOCK_ELEMENTS; + const uint64_t block_pos = position % BITMAP_BLOCK_ELEMENTS; + // Compute e(xclusive)rank (number of bits set to one before the given position, not included) + bitmap_block_t* const bitmap_block = bitmap->bitmap_blocks + block_num; + const uint64_t bitmap_masked = (block_pos!=0) ? bitmap_block->bitmap << (BITMAP_BLOCK_ELEMENTS - block_pos) : 0; + const uint64_t bitmap_count = POPCOUNT_64(bitmap_masked); + return bitmap_block->counter + bitmap_count; +} + + + diff --git a/src/lib/wfa2/utils/bitmap.h b/src/lib/wfa2/utils/bitmap.h new file mode 100644 index 000000000..98f122af0 --- /dev/null +++ b/src/lib/wfa2/utils/bitmap.h @@ -0,0 +1,106 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Basic bitmap datastructure (static) + */ + +#ifndef BITMAP_H_ +#define BITMAP_H_ + +/* + * Includes + */ +#include "../utils/commons.h" +#include "../system/mm_allocator.h" + +#define BITMAP_BLOCK_ELEMENTS 64 +#define BITMAP_BLOCK_MASK 0x0000000000000001ul + +/* + * Utils + */ +#define BITMAP_PREFETCH_BLOCK(bm,position) \ + PREFETCH(bm->bitmap_blocks+(position/BITMAP_BLOCK_ELEMENTS)) + +#define BITMAP_GET_BLOCK(bm,position,block_bitmap_ptr) \ + const uint64_t block_num = position / BITMAP_BLOCK_ELEMENTS; \ + uint64_t* const block_bitmap_ptr = &(bm->bitmap_blocks[block_num].bitmap) + +#define BM_BLOCK_IS_SET(block_bitmap,position) \ + (block_bitmap & (BITMAP_BLOCK_MASK << (position % BITMAP_BLOCK_ELEMENTS))) + +#define BM_BLOCK_SET(block_bitmap,position) \ + (block_bitmap |= (BITMAP_BLOCK_MASK << (position % BITMAP_BLOCK_ELEMENTS))) + +/* + * Bitmap + */ +typedef struct { + uint64_t counter; + uint64_t bitmap; +} bitmap_block_t; +typedef struct { + // Bitmap + uint64_t num_blocks; + bitmap_block_t* bitmap_blocks; + // MM + mm_allocator_t* mm_allocator; +} bitmap_t; + +/* + * Setup + */ +bitmap_t* bitmap_new( + const uint64_t length, + mm_allocator_t* const mm_allocator); +void bitmap_delete( + bitmap_t* const bitmap); + +/* + * Accessors + */ +void bitmap_set( + bitmap_t* const bitmap, + const uint64_t pos); +bool bitmap_is_set( + bitmap_t* const bitmap, + const uint64_t pos); +bool bitmap_check__set( + bitmap_t* const bitmap, + const uint64_t pos); + +/* + * Rank + */ +void bitmap_update_counters( + bitmap_t* const bitmap); +uint64_t bitmap_erank( + bitmap_t* const bitmap, + const uint64_t pos); + +#endif /* BITMAP_H_ */ diff --git a/src/lib/wfa2/utils/commons.c b/src/lib/wfa2/utils/commons.c new file mode 100644 index 000000000..450153b5e --- /dev/null +++ b/src/lib/wfa2/utils/commons.c @@ -0,0 +1,71 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Common functions/utilities and headers for C development + */ + +#include "commons.h" + +/* + * Random number generator [min,max) + */ +uint64_t rand_iid( + const uint64_t min, + const uint64_t max) { + const int n_rand = rand(); // [0, RAND_MAX] + const uint64_t range = max - min; + const uint64_t rem = RAND_MAX % range; + const uint64_t sample = RAND_MAX / range; + // Consider the small interval within remainder of RAND_MAX + if (n_rand < RAND_MAX - rem) { + return min + n_rand/sample; + } else { + return rand_iid(min,max); + } +} +/* + * Math + */ +uint32_t nominal_prop_u32( + const uint32_t base, + const double factor) { + if (0.0 <= factor && factor <= 1.0) { + return (uint32_t)((double)base*factor); + } else { + return (uint32_t)factor; + } +} +uint64_t nominal_prop_u64( + const uint64_t base, + const double factor) { + if (0.0 <= factor && factor <= 1.0) { + return (uint64_t)((double)base*factor); + } else { + return (uint64_t)factor; + } +} diff --git a/src/lib/wfa2/utils/commons.h b/src/lib/wfa2/utils/commons.h new file mode 100644 index 000000000..e3b5f2d24 --- /dev/null +++ b/src/lib/wfa2/utils/commons.h @@ -0,0 +1,287 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Common functions/utilities and headers for C development + */ + +#ifndef COMMONS_H_ +#define COMMONS_H_ + +#include +#include + +#ifndef WIN32 +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Macro Utils (Stringify) + */ +#define STRINGIFY_(a) #a +#define STRINGIFY(a) STRINGIFY_(a) +#define SWAP(a,b) do {__typeof__(a) aux = a; a = b; b = aux;} while (0) + +/* + * Special Characters + */ +#define EOS '\0' +#define EOL '\n' +#define TAB '\t' +#define DOS_EOL '\r' +#define PLUS '+' +#define MINUS '-' +#define FORMAT '%' +#define SPACE ' ' +#define SLASH '/' +#define STAR '*' +#define DOT '.' +#define COMA ',' +#define SEMICOLON ';' +#define COLON ':' +#define HASH '#' +#define UNDERSCORE '_' + +/* + * Metric Factors + */ +#define METRIC_FACTOR_1K (1000ul) +#define METRIC_FACTOR_1M (1000000ul) +#define METRIC_FACTOR_1G (1000000000ul) + +/* + * Number of lines + */ +#define NUM_LINES_1K (1000ul) +#define NUM_LINES_2K (2000ul) +#define NUM_LINES_5K (5000ul) +#define NUM_LINES_10K (10000ul) +#define NUM_LINES_20K (20000ul) +#define NUM_LINES_50K (50000ul) +#define NUM_LINES_100K (100000ul) +#define NUM_LINES_200K (200000ul) +#define NUM_LINES_500K (500000ul) +#define NUM_LINES_1M (1000000ul) +#define NUM_LINES_2M (2000000ul) +#define NUM_LINES_5M (5000000ul) +#define NUM_LINES_10M (10000000ul) +#define NUM_LINES_20M (20000000ul) +#define NUM_LINES_50M (50000000ul) + +/* + * Buffer sizes + */ +#define BUFFER_SIZE_1K (1ul<<10) +#define BUFFER_SIZE_2K (1ul<<11) +#define BUFFER_SIZE_4K (1ul<<12) +#define BUFFER_SIZE_8K (1ul<<13) +#define BUFFER_SIZE_16K (1ul<<14) +#define BUFFER_SIZE_32K (1ul<<15) +#define BUFFER_SIZE_64K (1ul<<16) +#define BUFFER_SIZE_128K (1ul<<17) +#define BUFFER_SIZE_256K (1ul<<18) +#define BUFFER_SIZE_512K (1ul<<19) +#define BUFFER_SIZE_1M (1ul<<20) +#define BUFFER_SIZE_2M (1ul<<21) +#define BUFFER_SIZE_4M (1ul<<22) +#define BUFFER_SIZE_8M (1ul<<23) +#define BUFFER_SIZE_16M (1ul<<24) +#define BUFFER_SIZE_32M (1ul<<25) +#define BUFFER_SIZE_64M (1ul<<26) +#define BUFFER_SIZE_128M (1ul<<27) +#define BUFFER_SIZE_256M (1ul<<28) +#define BUFFER_SIZE_512M (1ul<<29) +#define BUFFER_SIZE_1G (1ul<<30) +#define BUFFER_SIZE_2G (1ul<<31) +#define BUFFER_SIZE_4G (1ul<<32) +#define BUFFER_SIZE_8G (1ul<<33) +#define BUFFER_SIZE_16G (1ul<<34) +#define BUFFER_SIZE_32G (1ul<<35) +#define BUFFER_SIZE_64G (1ul<<36) +#define BUFFER_SIZE_128G (1ul<<37) +#define BUFFER_SIZE_256G (1ul<<38) +// Conversion utils +#define CONVERT_B_TO_KB(number) ((number)/(1024)) +#define CONVERT_B_TO_MB(number) ((number)/(1024*1024)) +#define CONVERT_B_TO_GB(number) ((number)/(1024*1024*1024)) + +/* + * BM sizes + */ +#define UINT512_LENGTH 512 +#define UINT512_SIZE 64 +#define UINT256_LENGTH 256 +#define UINT256_SIZE 32 +#define UINT128_LENGTH 128 +#define UINT128_SIZE 16 +#define UINT64_LENGTH 64 +#define UINT64_SIZE 8 +#define UINT32_LENGTH 32 +#define UINT32_SIZE 4 +#define UINT16_LENGTH 16 +#define UINT16_SIZE 2 +#define UINT8_LENGTH 8 +#define UINT8_SIZE 1 + +/* + * Common Masks + */ +#define UINT64_ZEROS 0x0000000000000000ull +#define UINT64_ONES 0xFFFFFFFFFFFFFFFFull +#define UINT32_ZEROS 0x00000000ul +#define UINT32_ONES 0xFFFFFFFFul +// Extraction masks +#define UINT64_ONE_MASK 0x0000000000000001ull +#define UINT64_ZERO_MASK 0xFFFFFFFFFFFFFFFEull +#define UINT64_ONE_LAST_MASK 0x8000000000000000ull +#define UINT64_ZERO_LAST_MASK 0x7FFFFFFFFFFFFFFFull +#define UINT32_ONE_MASK 0x00000001ul +#define UINT32_ZERO_MASK 0xFFFFFFFEul +#define UINT32_ONE_LAST_MASK 0x80000000ul +#define UINT32_ZERO_LAST_MASK 0x7FFFFFFFul +// Conversions/Extractions +#define UINT64_TO_UINT32_LSB(value) ((uint32_t)((value) & 0x00000000FFFFFFFFul)) +#define UINT64_TO_UINT32_MSB(value) ((uint32_t)((value) >> 32)) + +/* + * Common numerical data processing/formating + */ +#define MIN(a,b) (((a)<=(b))?(a):(b)) +#define MAX(a,b) (((a)>=(b))?(a):(b)) +#define ABS(a) (((a)>=0)?(a):-(a)) + +/* + * Pseudo-Random number generator + */ +#define rand_init() srand(time(0)) +#define rand_i(min,max) ( min + ( rand()%(max-min+1) ) ) +#define rand_f(min,max) ( min + ((double)rand()/(double)(RAND_MAX+1)) * (max-min+1) ) +uint64_t rand_iid(const uint64_t min,const uint64_t max); + +/* + * Parsing + */ +#define IS_NUMBER(character) ('0' <= (character) && (character) <= '9') +#define IS_DIGIT(character) IS_NUMBER(character) +#define IS_LETTER(character) (('a' <= (character) && (character) <= 'z') || ('A' <= (character) && (character) <= 'Z')) +#define IS_ALPHANUMERIC(character) (IS_NUMBER(character) || IS_LETTER(character)) +#define IS_BETWEEN(number,a,b) ((a)<=(number) && (number)<=(b)) + +#define IS_EOL(character) ((character)==EOL) +#define IS_ANY_EOL(character) ((character)==EOL || (character)==DOS_EOL) +#define IS_HEX_DIGIT(character) (IS_NUMBER(character) || ('a' <= (character) && (character) <= 'f') || ('A' <= (character) && (character) <= 'F')) + +#define IS_END_OF_RECORD(character) ( (character)==EOL || (character)==EOS ) +#define IS_END_OF_FIELD(character) ( IS_END_OF_RECORD(character) || (character)==SPACE || (character)==TAB ) + +#define GET_DIGIT(character) ((character) - '0') +#define GET_HEX_DIGIT(character) (IS_NUMBER(character) ? GET_DIGIT(character) : (toupper(character) - 'A' + 10)) + +/* + * Math + */ +#define BOUNDED_SUBTRACTION(minuend,subtrahend,limit) (((minuend)>((limit)+(subtrahend))) ? (minuend)-(subtrahend):(limit)) +#define BOUNDED_ADDITION(summand_A,summand_B,limit) ((((summand_A)+(summand_B))<(limit)) ? (summand_A)+(summand_B):(limit)) + +#define PERCENTAGE(AMOUNT,TOTAL) ((TOTAL)?100.0*(float)(AMOUNT)/(float)(TOTAL):0.0) +#define DIV_FLOOR(NUMERATOR,DENOMINATOR) ((NUMERATOR)/(DENOMINATOR)) +#define DIV_CEIL(NUMERATOR,DENOMINATOR) (((NUMERATOR)+((DENOMINATOR)-1))/(DENOMINATOR)) +#define DIVC_FLOOR(NUMERATOR,DENOMINATOR) ((DENOMINATOR) ? DIV_FLOOR(NUMERATOR,DENOMINATOR) :(0)) +#define DIVC_CEIL(NUMERATOR,DENOMINATOR) ((DENOMINATOR) ? DIV_CEIL(NUMERATOR,DENOMINATOR) :(0)) + +#define TELESCOPIC_FACTOR (3.0/2.0) + +uint32_t nominal_prop_u32(const uint32_t base,const double factor); +uint64_t nominal_prop_u64(const uint64_t base,const double factor); + +/* + * Inline + */ +#define FORCE_INLINE __attribute__((always_inline)) inline +#define FORCE_NO_INLINE __attribute__ ((noinline)) + +/* + * Vectorize + */ +#if defined(__clang__) + #define PRAGMA_LOOP_VECTORIZE _Pragma("clang loop vectorize(enable)") +#elif defined(__GNUC__) + #define PRAGMA_LOOP_VECTORIZE _Pragma("GCC ivdep") +#else + #define PRAGMA_LOOP_VECTORIZE _Pragma("ivdep") +#endif + +/* + * Popcount macros + */ +#define POPCOUNT_64(word64) __builtin_popcountll((word64)) +#define POPCOUNT_32(word32) __builtin_popcount((word32)) + +/* + * Prefetch macros + */ +#define PREFETCH(ADDR) __builtin_prefetch(((const char*)ADDR)) + +/* + * Display + */ +#define PRINT_CHAR_REP(stream,character,times) { \ + int i; \ + for (i=0;i + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: DNA text encoding/decoding utils + */ + +/* + * Pragmas + */ +#ifdef __clang__ +#pragma GCC diagnostic ignored "-Winitializer-overrides" +#endif + +/* + * Include + */ +#include "../utils/dna_text.h" + +/* + * Tables/Conversions Implementation + */ +const uint8_t dna_encode_table[256] = +{ + [0 ... 255] = 4, + ['A'] = 0, ['C'] = 1, ['G'] = 2, ['T'] = 3, ['N'] = 4, + ['a'] = 0, ['c'] = 1, ['g'] = 2, ['t'] = 3, ['n'] = 4, +}; +const char dna_decode_table[DNA_EXTENDED_RANGE] = +{ + [ENC_DNA_CHAR_A] = DNA_CHAR_A, + [ENC_DNA_CHAR_C] = DNA_CHAR_C, + [ENC_DNA_CHAR_G] = DNA_CHAR_G, + [ENC_DNA_CHAR_T] = DNA_CHAR_T, + [ENC_DNA_CHAR_N] = DNA_CHAR_N, +}; + diff --git a/src/lib/wfa2/utils/dna_text.h b/src/lib/wfa2/utils/dna_text.h new file mode 100644 index 000000000..9fd135b04 --- /dev/null +++ b/src/lib/wfa2/utils/dna_text.h @@ -0,0 +1,76 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: DNA text encoding/decoding utils + */ + + +#ifndef DNA_TEXT_H_ +#define DNA_TEXT_H_ + +#include "../utils/commons.h" + +/* + * Range of DNA Nucleotides + */ +#define DNA_RANGE 4 +#define DNA_EXTENDED_RANGE 5 + +#define DNA_RANGE_BITS 2 + +/* + * DNA Nucleotides + */ +#define DNA_CHAR_A 'A' +#define DNA_CHAR_C 'C' +#define DNA_CHAR_G 'G' +#define DNA_CHAR_T 'T' +#define DNA_CHAR_N 'N' + +/* + * Encoded DNA Nucleotides + */ +#define ENC_DNA_CHAR_A 0 +#define ENC_DNA_CHAR_C 1 +#define ENC_DNA_CHAR_G 2 +#define ENC_DNA_CHAR_T 3 +#define ENC_DNA_CHAR_N 4 + +/* + * Translation tables + */ +extern const uint8_t dna_encode_table[256]; +extern const char dna_decode_table[DNA_EXTENDED_RANGE]; + +/* + * Translation functions + */ +#define dna_encode(character) (dna_encode_table[(int)(character)]) +#define dna_decode(enc_char) (dna_decode_table[(int)(enc_char)]) + +#endif /* DNA_TEXT_H_ */ diff --git a/src/lib/wfa2/utils/heatmap.c b/src/lib/wfa2/utils/heatmap.c new file mode 100644 index 000000000..e7e962ca3 --- /dev/null +++ b/src/lib/wfa2/utils/heatmap.c @@ -0,0 +1,173 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + */ + +#include "heatmap.h" + +/* + * Constants + */ +#define HEATMAP_INT_MIN INT_MIN +#define HEATMAP_INT_MAX INT_MAX +#define HEATMAP_SEPARATOR ',' + +/* + * Setup + */ +heatmap_t* heatmap_new( + const heatmap_type type, + const int min_v, + const int max_v, + const int min_h, + const int max_h, + const int resolution_points) { + // Alloc + heatmap_t* const heatmap = (heatmap_t*)malloc(sizeof(heatmap_t)); + // Configure + heatmap->type = type; + heatmap->min_v = min_v; + heatmap->max_v = max_v; + heatmap->min_h = min_h; + heatmap->max_h = max_h; + // Binning (mantain aspect ratio) + const int v_range = (max_v-min_v+1); + const int h_range = (max_h-min_h+1); + const int max_range = MAX(v_range,h_range); + if (max_range <= resolution_points) { + heatmap->binning_factor = 1.0f; + heatmap->num_rows = v_range; + heatmap->num_columns = h_range; + } else { + heatmap->binning_factor = (float)max_range/(float)resolution_points; + heatmap->num_rows = (float)v_range/heatmap->binning_factor; + heatmap->num_columns = (float)h_range/heatmap->binning_factor; + } + // Allocate matrix + heatmap->values = (int**)malloc(heatmap->num_rows*sizeof(int*)); + int i; + for (i=0;inum_rows;++i) { + heatmap->values[i] = (int*)malloc(heatmap->num_columns*sizeof(int)); // Allocate row + } + // Clear + heatmap_clear(heatmap); + // Return + return heatmap; +} +void heatmap_clear( + heatmap_t* const heatmap) { + // Parameters + const heatmap_type type = heatmap->type; + const int num_rows = heatmap->num_rows; + const int num_columns = heatmap->num_columns; + // Clear values + int i, j; + for (i=0;ivalues[i][j] = HEATMAP_INT_MAX; + break; + } + case heatmap_max: + case heatmap_value: + default: { + for (j=0;jvalues[i][j] = HEATMAP_INT_MIN; + break; + } + } + } +} +void heatmap_delete( + heatmap_t* const heatmap) { + int i; + for (i=0;inum_rows;++i) { + free(heatmap->values[i]); + } + free(heatmap->values); + free(heatmap); +} +/* + * Accessors + */ +void heatmap_set( + heatmap_t* const heatmap, + const int v, + const int h, + const int value) { + // Paramters + const int num_rows = heatmap->num_rows; + const int num_columns = heatmap->num_columns; + // Check position + if (vmin_v || v>heatmap->max_v) return; + if (hmin_h || h>heatmap->max_h) return; + // Adjust position into binning + int v_adjusted = (float)(v - heatmap->min_v) / heatmap->binning_factor; + int h_adjusted = (float)(h - heatmap->min_h) / heatmap->binning_factor; + if (v_adjusted >= num_rows) v_adjusted = num_rows-1; + if (h_adjusted >= num_columns) h_adjusted = num_columns-1; + // Set value + switch (heatmap->type) { + case heatmap_min: + heatmap->values[v_adjusted][h_adjusted] = MIN(heatmap->values[v_adjusted][h_adjusted],value); + break; + case heatmap_max: + heatmap->values[v_adjusted][h_adjusted] = MAX(heatmap->values[v_adjusted][h_adjusted],value); + break; + case heatmap_value: + default: + heatmap->values[v_adjusted][h_adjusted] = value; + break; + } +} +/* + * Display + */ +void heatmap_print( + FILE* const stream, + heatmap_t* const heatmap) { + // Parameters + const int num_rows = heatmap->num_rows; + const int num_columns = heatmap->num_columns; + // Print values + int v, h; + for (v=0;v0) fprintf(stream,"%c",HEATMAP_SEPARATOR); + const int value = heatmap->values[v][h]; + if (value == HEATMAP_INT_MIN || value == HEATMAP_INT_MAX) { + fprintf(stream,"-1"); + } else { + fprintf(stream,"%d",value); + } + } + fprintf(stream,"\n"); + } +} + + + diff --git a/src/lib/wfa2/utils/heatmap.h b/src/lib/wfa2/utils/heatmap.h new file mode 100644 index 000000000..bd2e8b37b --- /dev/null +++ b/src/lib/wfa2/utils/heatmap.h @@ -0,0 +1,91 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + */ + +#ifndef HEATMAP_H_ +#define HEATMAP_H_ + +#include "../utils/commons.h" + +/* + * Heatmap + */ +typedef enum { + heatmap_min, // Min value stays + heatmap_max, // Max value stays + heatmap_value, // Last value set stays +} heatmap_type; +typedef struct { + // Configuration + heatmap_type type; + // Dimensions + int num_rows; + int num_columns; + // Range + int min_v; + int max_v; + int min_h; + int max_h; + float binning_factor; + // Data + int** values; +} heatmap_t; + +/* + * Setup + */ +heatmap_t* heatmap_new( + const heatmap_type type, + const int min_v, + const int max_v, + const int min_h, + const int max_h, + const int resolution_points); +void heatmap_clear( + heatmap_t* const heatmap); +void heatmap_delete( + heatmap_t* const heatmap); + +/* + * Accessors + */ +void heatmap_set( + heatmap_t* const heatmap, + const int v, + const int h, + const int value); + +/* + * Display + */ +void heatmap_print( + FILE* const stream, + heatmap_t* const heatmap); + +#endif /* HEATMAP_H_ */ diff --git a/src/lib/wfa2/utils/sequence_buffer.c b/src/lib/wfa2/utils/sequence_buffer.c new file mode 100644 index 000000000..4470868ba --- /dev/null +++ b/src/lib/wfa2/utils/sequence_buffer.c @@ -0,0 +1,134 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Simple linear vector for generic type elements + */ + +#include "../utils/sequence_buffer.h" + +/* + * Setup + */ +sequence_buffer_t* sequence_buffer_new( + const uint64_t num_sequences_hint, + const uint64_t sequence_length_hint) { + // Alloc + sequence_buffer_t* const sequence_buffer = malloc(sizeof(sequence_buffer_t)); + // ID + sequence_buffer->sequence_id = 1; + // Initialize sequences + sequence_buffer->offsets = malloc(num_sequences_hint*sizeof(sequence_offset_t)); + sequence_buffer->offsets_used = 0; + sequence_buffer->offsets_allocated = num_sequences_hint; + // Initialize buffer + const uint64_t buffer_size = num_sequences_hint*sequence_length_hint; + sequence_buffer->buffer = malloc(buffer_size); + sequence_buffer->buffer_used = 0; + sequence_buffer->buffer_allocated = buffer_size; + // Stats + sequence_buffer->max_pattern_length = 0; + sequence_buffer->max_text_length = 0; + // Return + return sequence_buffer; +} +void sequence_buffer_clear( + sequence_buffer_t* const sequence_buffer) { + sequence_buffer->sequence_id = 1; + sequence_buffer->offsets_used = 0; + sequence_buffer->buffer_used = 0; + sequence_buffer->max_pattern_length = 0; + sequence_buffer->max_text_length = 0; +} +void sequence_buffer_delete( + sequence_buffer_t* const sequence_buffer) { + free(sequence_buffer->buffer); + free(sequence_buffer->offsets); + free(sequence_buffer); +} +/* + * Accessors + */ +void sequence_buffer_add_offsets( + sequence_buffer_t* const sequence_buffer, + const uint64_t pattern_offset, + const uint64_t pattern_length, + const uint64_t text_offset, + const uint64_t text_length) { + // Check allocated memory + if (sequence_buffer->offsets_used == sequence_buffer->offsets_allocated) { + const uint64_t num_offsets = (float)(sequence_buffer->offsets_used+1) * (3.0/2.0); + sequence_buffer->offsets = realloc(sequence_buffer->offsets,num_offsets*sizeof(sequence_offset_t)); + sequence_buffer->offsets_allocated = num_offsets; + } + // Add sequence pair + sequence_offset_t* const current_offsets = sequence_buffer->offsets + sequence_buffer->offsets_used; + current_offsets->pattern_offset = pattern_offset; + current_offsets->pattern_length = pattern_length; + current_offsets->text_offset = text_offset; + current_offsets->text_length = text_length; + sequence_buffer->offsets_used += 1; +} +void sequence_buffer_add_pair( + sequence_buffer_t* const sequence_buffer, + char* const pattern, + const uint64_t pattern_length, + char* const text, + const uint64_t text_length) { + // Allocate memory + const uint64_t bytes_required = pattern_length + text_length + 4; // Padding + if (sequence_buffer->buffer_used+bytes_required > sequence_buffer->buffer_allocated) { + const uint64_t proposed_buffer_size = (3*(sequence_buffer->buffer_used+bytes_required))/2; + sequence_buffer->buffer_allocated = proposed_buffer_size; + sequence_buffer->buffer = realloc(sequence_buffer->buffer,proposed_buffer_size); + } + // Copy pattern sequence + char* mem_ptr = sequence_buffer->buffer + sequence_buffer->buffer_used; + memcpy(mem_ptr,pattern,pattern_length); + mem_ptr += pattern_length; + mem_ptr[0] = '\0'; + mem_ptr[1] = '!'; + mem_ptr += 2; // Padding sentinels + // Copy text sequence + memcpy(mem_ptr,text,text_length); + mem_ptr += text_length; + mem_ptr[0] = '\0'; + mem_ptr[1] = '?'; + mem_ptr += 2; // Padding sentinels + // Add sequence pair + const uint64_t pattern_offset = sequence_buffer->buffer_used; + const uint64_t text_offset = pattern_offset + pattern_length + 2; + sequence_buffer_add_offsets( + sequence_buffer,pattern_offset, + pattern_length,text_offset,text_length); + // Update used + sequence_buffer->buffer_used += bytes_required; + // Update stats + sequence_buffer->max_pattern_length = MAX(sequence_buffer->max_pattern_length,pattern_length); + sequence_buffer->max_text_length = MAX(sequence_buffer->max_text_length,text_length); +} + diff --git a/src/lib/wfa2/utils/sequence_buffer.h b/src/lib/wfa2/utils/sequence_buffer.h new file mode 100644 index 000000000..9ecc77bf0 --- /dev/null +++ b/src/lib/wfa2/utils/sequence_buffer.h @@ -0,0 +1,80 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + */ + +#ifndef SEQUENCE_BUFFER_H_ +#define SEQUENCE_BUFFER_H_ + +#include "../utils/commons.h" +#include "../system/mm_allocator.h" + +typedef struct { + uint64_t pattern_offset; + uint64_t pattern_length; + uint64_t text_offset; + uint64_t text_length; +} sequence_offset_t; +typedef struct { + // ID + uint64_t sequence_id; + // Sequences + sequence_offset_t* offsets; + uint64_t offsets_used; + uint64_t offsets_allocated; + // Buffer + char* buffer; + uint64_t buffer_used; + uint64_t buffer_allocated; + // Stats + int max_pattern_length; + int max_text_length; +} sequence_buffer_t; + +/* + * Setup + */ +sequence_buffer_t* sequence_buffer_new( + const uint64_t num_sequences_hint, + const uint64_t sequence_length_hint); +void sequence_buffer_clear( + sequence_buffer_t* const sequence_buffer); +void sequence_buffer_delete( + sequence_buffer_t* const sequence_buffer); + +/* + * Accessors + */ +void sequence_buffer_add_pair( + sequence_buffer_t* const sequence_buffer, + char* const pattern, + const uint64_t pattern_length, + char* const text, + const uint64_t text_length); + +#endif /* SEQUENCE_BUFFER_H_ */ diff --git a/src/lib/wfa2/utils/string_padded.c b/src/lib/wfa2/utils/string_padded.c new file mode 100644 index 000000000..90885b4ae --- /dev/null +++ b/src/lib/wfa2/utils/string_padded.c @@ -0,0 +1,139 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Padded string module to avoid handling corner conditions + */ + +#include "../utils/string_padded.h" +#include "../system/mm_allocator.h" + +/* + * Strings (text/pattern) padded + */ +void strings_padded_add_padding( + const char* const buffer, + const int buffer_length, + const int begin_padding_length, + const int end_padding_length, + const char padding_value, + char** const buffer_padded, + char** const buffer_padded_begin, + const bool reverse_sequence, + mm_allocator_t* const mm_allocator) { + // Allocate + const int buffer_padded_length = begin_padding_length + buffer_length + end_padding_length; + *buffer_padded = mm_allocator_malloc(mm_allocator,buffer_padded_length); + // Add begin padding + memset(*buffer_padded,padding_value,begin_padding_length); + // Copy buffer + *buffer_padded_begin = *buffer_padded + begin_padding_length; + if (reverse_sequence) { + int i; + for (i=0;imm_allocator = mm_allocator; + // Compute padding dimensions + const int pattern_begin_padding_length = 0; + const int pattern_end_padding_length = padding_length; + const int text_begin_padding_length = 0; + const int text_end_padding_length = padding_length; + // Add padding + strings_padded_add_padding( + pattern,pattern_length, + pattern_begin_padding_length,pattern_end_padding_length,'?', + &(strings_padded->pattern_padded_buffer), + &(strings_padded->pattern_padded), + reverse_sequences,mm_allocator); + strings_padded_add_padding( + text,text_length, + text_begin_padding_length,text_end_padding_length,'!', + &(strings_padded->text_padded_buffer), + &(strings_padded->text_padded), + reverse_sequences,mm_allocator); + // Return + return strings_padded; +} +strings_padded_t* strings_padded_new_rhomb( + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + const int padding_length, + const bool reverse_sequences, + mm_allocator_t* const mm_allocator) { + // Allocate + strings_padded_t* const strings_padded = + mm_allocator_alloc(mm_allocator,strings_padded_t); + strings_padded->mm_allocator = mm_allocator; + // Compute padding dimensions + const int pattern_begin_padding_length = text_length + padding_length; + const int pattern_end_padding_length = pattern_length + text_length + padding_length; + const int text_begin_padding_length = padding_length; + const int text_end_padding_length = text_length + padding_length; + // Add padding + strings_padded_add_padding( + pattern,pattern_length, + pattern_begin_padding_length,pattern_end_padding_length,'?', + &(strings_padded->pattern_padded_buffer), + &(strings_padded->pattern_padded), + reverse_sequences,mm_allocator); + strings_padded_add_padding( + text,text_length, + text_begin_padding_length,text_end_padding_length,'!', + &(strings_padded->text_padded_buffer), + &(strings_padded->text_padded), + reverse_sequences,mm_allocator); + // Set lengths + strings_padded->pattern_length = pattern_length; + strings_padded->text_length = text_length; + // Return + return strings_padded; +} +void strings_padded_delete(strings_padded_t* const strings_padded) { + mm_allocator_free(strings_padded->mm_allocator,strings_padded->pattern_padded_buffer); + mm_allocator_free(strings_padded->mm_allocator,strings_padded->text_padded_buffer); + mm_allocator_free(strings_padded->mm_allocator,strings_padded); +} diff --git a/src/lib/wfa2/utils/string_padded.h b/src/lib/wfa2/utils/string_padded.h new file mode 100644 index 000000000..f0b268f37 --- /dev/null +++ b/src/lib/wfa2/utils/string_padded.h @@ -0,0 +1,79 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Padded string module to avoid handling corner conditions + */ + +#ifndef STRING_PADDED_H_ +#define STRING_PADDED_H_ + +/* + * Includes + */ +#include "../utils/commons.h" +#include "../system/mm_allocator.h" + +/* + * Strings Padded + */ +typedef struct { + // Dimensions + int pattern_length; + int text_length; + // Padded strings + char* pattern_padded; + char* text_padded; + // MM + char* pattern_padded_buffer; + char* text_padded_buffer; + mm_allocator_t* mm_allocator; +} strings_padded_t; + +/* + * Strings (text/pattern) padded + */ +strings_padded_t* strings_padded_new( + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + const int padding_length, + const bool reverse_sequences, + mm_allocator_t* const mm_allocator); +strings_padded_t* strings_padded_new_rhomb( + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + const int padding_length, + const bool reverse_sequences, + mm_allocator_t* const mm_allocator); +void strings_padded_delete( + strings_padded_t* const strings_padded); + +#endif /* STRING_PADDED_H_ */ diff --git a/src/lib/wfa2/utils/vector.c b/src/lib/wfa2/utils/vector.c new file mode 100644 index 000000000..5870dac80 --- /dev/null +++ b/src/lib/wfa2/utils/vector.c @@ -0,0 +1,125 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * VERSION: v20.08.25 + * DESCRIPTION: Simple linear vector (generic type elements) + */ + +#include "vector.h" + +/* + * Constants + */ +#define VECTOR_EXPAND_FACTOR (3.0/2.0) + +/* + * Setup + */ +vector_t* vector_new_( + const uint64_t num_initial_elements, + const uint64_t element_size) { + vector_t* const vector_buffer = (vector_t*) malloc(sizeof(vector_t)); + vector_buffer->element_size = element_size; + vector_buffer->elements_allocated = num_initial_elements; + vector_buffer->memory = malloc(num_initial_elements*element_size); + if (!vector_buffer->memory) { + fprintf(stderr,"Could not create new vector (%" PRIu64 " bytes requested)", + num_initial_elements*element_size); + exit(1); + } + vector_buffer->used = 0; + return vector_buffer; +} +void vector_delete( + vector_t* const vector) { + free(vector->memory); + free(vector); +} +void vector_cast( + vector_t* const vector, + const uint64_t element_size) { + vector->elements_allocated = (vector->elements_allocated*vector->element_size)/element_size; + vector->element_size = element_size; + vector->used = 0; +} +void vector_reserve( + vector_t* const vector, + const uint64_t num_elements, + const bool zero_mem) { + if (vector->elements_allocated < num_elements) { + const uint64_t proposed = (float)vector->elements_allocated*VECTOR_EXPAND_FACTOR; + vector->elements_allocated = num_elements>proposed?num_elements:proposed; + vector->memory = realloc(vector->memory,vector->elements_allocated*vector->element_size); + if (!vector->memory) { + fprintf(stderr,"Could not reserve vector (%" PRIu64 " bytes requested)", + vector->elements_allocated*vector->element_size); + exit(1); + } + } + if (zero_mem) { + memset(vector->memory+vector->used*vector->element_size,0, + (vector->elements_allocated-vector->used)*vector->element_size); + } +} +/* + * Accessors + */ +#ifdef VECTOR_DEBUG +void* vector_get_mem_element( + vector_t* const vector, + const uint64_t position, + const uint64_t element_size) { + if (position >= (vector)->used) { + fprintf(stderr,"Vector position out-of-range [0,%"PRIu64")",(vector)->used); + exit(1); + } + return vector->memory + (position*element_size); +} +#endif +/* + * Miscellaneous + */ +void vector_copy( + vector_t* const vector_to, + vector_t* const vector_from) { + // Prepare + vector_cast(vector_to,vector_from->element_size); + vector_reserve(vector_to,vector_from->used,false); + // Copy + vector_set_used(vector_to,vector_from->used); + memcpy(vector_to->memory,vector_from->memory,vector_from->used*vector_from->element_size); +} +vector_t* vector_dup( + vector_t* const vector_src) { + vector_t* const vector_cpy = vector_new_(vector_src->used,vector_src->element_size); + // Copy + vector_set_used(vector_cpy,vector_src->used); + memcpy(vector_cpy->memory,vector_src->memory,vector_src->used*vector_src->element_size); + return vector_cpy; +} + diff --git a/src/lib/wfa2/utils/vector.h b/src/lib/wfa2/utils/vector.h new file mode 100644 index 000000000..c63274763 --- /dev/null +++ b/src/lib/wfa2/utils/vector.h @@ -0,0 +1,144 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * VERSION: v20.08.25 + * DESCRIPTION: Simple linear vector for generic type elements + */ + +#ifndef VECTOR_H_ +#define VECTOR_H_ + +#include "commons.h" + +/* + * Checkers + */ +//#define VECTOR_DEBUG + +/* + * Data Structures + */ +typedef struct { + void* memory; + uint64_t used; + uint64_t element_size; + uint64_t elements_allocated; +} vector_t; + +/* + * Vector Setup (Initialization & Allocation) + */ +vector_t* vector_new_( + const uint64_t num_initial_elements, + const uint64_t element_size); +#define vector_new(num_initial_elements,type) vector_new_(num_initial_elements,sizeof(type)) +#define vector_clear(vector) (vector)->used=0 +void vector_delete( + vector_t* const vector); + +void vector_cast( + vector_t* const vector, + const uint64_t element_size); +void vector_reserve( + vector_t* const vector, + const uint64_t num_elements, + const bool zero_mem); +#define vector_reserve_additional(vector,additional) \ + vector_reserve(vector,vector_get_used(vector)+(additional),false) +#define vector_prepare(vector,num_elements,type) \ + vector_cast(vector,sizeof(type)); \ + vector_reserve(vector,num_elements,false); + +/* + * Element Getters/Setters + */ +#define vector_is_empty(vector) (vector_get_used(vector)==0) +#define vector_get_mem(vector,type) ((type*)((vector)->memory)) +#define vector_get_last_elm(vector,type) (vector_get_mem(vector,type)+(vector)->used-1) +#define vector_get_free_elm(vector,type) (vector_get_mem(vector,type)+(vector)->used) +#define vector_set_elm(vector,position,type,elm) *vector_get_elm(vector,position,type) = (elm) +#ifndef VECTOR_DEBUG + #define vector_get_elm(vector,position,type) (vector_get_mem(vector,type)+position) +#else + void* vector_get_mem_element( + vector_t* const vector, + const uint64_t position, + const uint64_t element_size); + #define vector_get_elm(vector,position,type) ((type*)vector_get_mem_element(vector,position,sizeof(type))) +#endif + +/* + * Used elements Getters/Setters + */ +#define vector_get_used(vector) ((vector)->used) +#define vector_set_used(vector,total_used) (vector)->used=(total_used) +#define vector_inc_used(vector) (++((vector)->used)) +#define vector_dec_used(vector) (--((vector)->used)) +#define vector_add_used(vector,additional) vector_set_used(vector,vector_get_used(vector)+additional) + +/* + * Vector Allocate/Insert (Get a new element or Add an element to the end of the vector) + */ +#define vector_alloc_new(vector,type,return_element_pointer) { \ + vector_reserve_additional(vector,1); \ + return_element_pointer = vector_get_free_elm(vector,type); \ + vector_inc_used(vector); \ +} +#define vector_insert(vector,element,type) { \ + vector_reserve_additional(vector,1); \ + *(vector_get_free_elm(vector,type)) = element; \ + vector_inc_used(vector); \ +} + +/* + * Macro generic iterator + * VECTOR_ITERATE(vector_of_ints,elm_iterator,elm_counter,int) { + * ..code.. + * } + */ +#define VECTOR_ITERATE(vector,element,counter,type) \ + const uint64_t vector_##element##_used = vector_get_used(vector); \ + type* element = vector_get_mem(vector,type); \ + uint64_t counter; \ + for (counter=0;counter + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Individual WaveFront data structure + */ + +#include "wavefront.h" + +/* + * Setup + */ +void wavefront_allocate( + wavefront_t* const wavefront, + const int wf_elements_allocated, + const bool allocate_backtrace, + mm_allocator_t* const mm_allocator) { + // Allocate memory + wavefront->wf_elements_allocated = wf_elements_allocated; + wavefront->offsets_mem = mm_allocator_calloc( + mm_allocator,wf_elements_allocated,wf_offset_t,false); + if (allocate_backtrace) { + wavefront->bt_pcigar_mem = mm_allocator_calloc( + mm_allocator,wf_elements_allocated,pcigar_t,false); + wavefront->bt_prev_mem = mm_allocator_calloc( + mm_allocator,wf_elements_allocated,bt_block_idx_t,false); + } else { + wavefront->bt_pcigar_mem = NULL; + } +} +void wavefront_resize( + wavefront_t* const wavefront, + const int wf_elements_allocated, + mm_allocator_t* const mm_allocator) { + // Set new size + wavefront->wf_elements_allocated = wf_elements_allocated; + // Reallocate offsets (Content is lost) + mm_allocator_free(mm_allocator,wavefront->offsets_mem); + wavefront->offsets_mem = mm_allocator_calloc( + mm_allocator,wf_elements_allocated,wf_offset_t,false); + // Reallocate backtrace (Content is lost) + if (wavefront->bt_pcigar_mem) { + mm_allocator_free(mm_allocator,wavefront->bt_pcigar_mem); + mm_allocator_free(mm_allocator,wavefront->bt_prev_mem); + wavefront->bt_pcigar_mem = mm_allocator_calloc( + mm_allocator,wf_elements_allocated,pcigar_t,false); + wavefront->bt_prev_mem = mm_allocator_calloc( + mm_allocator,wf_elements_allocated,bt_block_idx_t,false); + } +} +void wavefront_free( + wavefront_t* const wavefront, + mm_allocator_t* const mm_allocator) { + mm_allocator_free(mm_allocator,wavefront->offsets_mem); + if (wavefront->bt_pcigar_mem) { + mm_allocator_free(mm_allocator,wavefront->bt_pcigar_mem); + mm_allocator_free(mm_allocator,wavefront->bt_prev_mem); + } +} +/* + * Initialization + */ +void wavefront_init( + wavefront_t* const wavefront, + const int min_lo, + const int max_hi) { + // Limits + wavefront->null = false; + wavefront->lo = 1; + wavefront->hi = -1; + // Elements + wavefront->offsets = wavefront->offsets_mem - min_lo; // Center at k=0 + if (wavefront->bt_pcigar_mem) { + wavefront->bt_occupancy_max = 0; + wavefront->bt_pcigar = wavefront->bt_pcigar_mem - min_lo; // Center at k=0 + wavefront->bt_prev = wavefront->bt_prev_mem - min_lo; // Center at k=0 + } + // Internals + wavefront->wf_elements_allocated_min = min_lo; + wavefront->wf_elements_allocated_max = max_hi; + wavefront->wf_elements_init_min = 0; + wavefront->wf_elements_init_max = 0; +} +void wavefront_init_null( + wavefront_t* const wavefront, + const int min_lo, + const int max_hi) { + // Limits + wavefront->null = true; + wavefront->lo = 1; + wavefront->hi = -1; + // Elements + wavefront->offsets = wavefront->offsets_mem - min_lo; // Center at k=0 + if (wavefront->bt_pcigar_mem) { + wavefront->bt_occupancy_max = 0; + wavefront->bt_pcigar = wavefront->bt_pcigar_mem - min_lo; // Center at k=0 + wavefront->bt_prev = wavefront->bt_prev_mem - min_lo; // Center at k=0 + } + // Initialize + const int wf_elements = WAVEFRONT_LENGTH(min_lo,max_hi); + int i; + for (i=0;ioffsets_mem[i] = WAVEFRONT_OFFSET_NULL; + } + if (wavefront->bt_pcigar_mem) { // TODO: Really needed? + memset(wavefront->bt_pcigar_mem,0,wf_elements*sizeof(pcigar_t)); + memset(wavefront->bt_prev_mem,0,wf_elements*sizeof(bt_block_idx_t)); + } + // Internals + wavefront->wf_elements_allocated_min = min_lo; + wavefront->wf_elements_allocated_max = max_hi; + wavefront->wf_elements_init_min = min_lo; + wavefront->wf_elements_init_max = max_hi; +} +void wavefront_init_victim( + wavefront_t* const wavefront, + const int min_lo, + const int max_hi) { + // Delegate init + wavefront_init(wavefront,min_lo,max_hi); + // Set Null + wavefront->null = true; +} +/* + * Accessors + */ +void wavefront_set_limits( + wavefront_t* const wavefront, + const int lo, + const int hi) { + // Set effective limits + wavefront->lo = lo; + wavefront->hi = hi; + // Set initialization limits (all elms beyond are treated as Nulls) + wavefront->wf_elements_init_min = lo; + wavefront->wf_elements_init_max = hi; +} +/* + * Utils + */ +uint64_t wavefront_get_size( + wavefront_t* const wavefront) { + uint64_t total_size = wavefront->wf_elements_allocated*sizeof(wf_offset_t); + if (wavefront->bt_pcigar_mem) { + total_size += wavefront->wf_elements_allocated*(sizeof(pcigar_t)+sizeof(bt_block_idx_t)); + } + return total_size; +} + + + + diff --git a/src/lib/wfa2/wavefront/wavefront.h b/src/lib/wfa2/wavefront/wavefront.h new file mode 100644 index 000000000..ad0351a51 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront.h @@ -0,0 +1,146 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Individual WaveFront data structure + */ + +#ifndef WAVEFRONT_H_ +#define WAVEFRONT_H_ + +#include "../utils/commons.h" +#include "../system/mm_allocator.h" +#include "wavefront_offset.h" +#include "wavefront_backtrace_buffer.h" + +/* + * Alignment position + */ +typedef struct { + int score; // Score + int k; // Diagonal + wf_offset_t offset; // Offset +} wavefront_pos_t; + +/* + * Wavefront + */ +typedef enum { + wavefront_status_free, + wavefront_status_busy, + wavefront_status_deallocated, +} wavefront_status_type; +typedef struct { + // Dimensions + bool null; // Is null interval? + int lo; // Lowest diagonal (inclusive) + int hi; // Highest diagonal (inclusive) + // Wavefront elements + wf_offset_t* offsets; // Offsets (k-centered) + wf_offset_t* offsets_mem; // Offsets base memory (Internal) + // Piggyback backtrace + int bt_occupancy_max; // Maximum number of pcigar-ops stored on the Backtrace-block + pcigar_t* bt_pcigar; // Backtrace-block pcigar (k-centered) + bt_block_idx_t* bt_prev; // Backtrace-block previous-index (k-centered) + pcigar_t* bt_pcigar_mem; // Backtrace-block (base memory - Internal) + bt_block_idx_t* bt_prev_mem; // Backtrace-block previous-index (base memory - Internal) + // Slab internals + wavefront_status_type status; // Wavefront status (memory state) + int wf_elements_allocated; // Total wf-elements allocated (max. wf. size) + int wf_elements_allocated_min; // Minimum diagonal-element wf-element allocated + int wf_elements_allocated_max; // Maximum diagonal-element wf-element allocated + int wf_elements_init_min; // Minimum diagonal-element initialized (inclusive) + int wf_elements_init_max; // Maximum diagonal-element initialized (inclusive) +} wavefront_t; + +/* + * Wavefront Set + */ +typedef struct { + /* In Wavefronts*/ + wavefront_t* in_mwavefront_misms; + wavefront_t* in_mwavefront_open1; + wavefront_t* in_mwavefront_open2; + wavefront_t* in_i1wavefront_ext; + wavefront_t* in_i2wavefront_ext; + wavefront_t* in_d1wavefront_ext; + wavefront_t* in_d2wavefront_ext; + /* Out Wavefronts */ + wavefront_t* out_mwavefront; + wavefront_t* out_i1wavefront; + wavefront_t* out_i2wavefront; + wavefront_t* out_d1wavefront; + wavefront_t* out_d2wavefront; +} wavefront_set_t; + +/* + * Setup + */ +void wavefront_allocate( + wavefront_t* const wavefront, + const int wf_elements_allocated, + const bool allocate_backtrace, + mm_allocator_t* const mm_allocator); +void wavefront_resize( + wavefront_t* const wavefront, + const int wf_elements_allocated, + mm_allocator_t* const mm_allocator); +void wavefront_free( + wavefront_t* const wavefront, + mm_allocator_t* const mm_allocator); + +/* + * Initialization + */ +void wavefront_init( + wavefront_t* const wavefront, + const int min_lo, + const int max_hi); +void wavefront_init_null( + wavefront_t* const wavefront, + const int min_lo, + const int max_hi); +void wavefront_init_victim( + wavefront_t* const wavefront, + const int min_lo, + const int max_hi); + +/* + * Accessors + */ +void wavefront_set_limits( + wavefront_t* const wavefront, + const int lo, + const int hi); + +/* + * Utils + */ +uint64_t wavefront_get_size( + wavefront_t* const wavefront); + +#endif /* WAVEFRONT_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_align.c b/src/lib/wfa2/wavefront/wavefront_align.c new file mode 100644 index 000000000..544d39bd2 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_align.c @@ -0,0 +1,197 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for sequence pairwise alignment + */ + +#include "wavefront_align.h" +#include "wavefront_unialign.h" +#include "wavefront_bialign.h" +#include "wavefront_compute.h" +#include "wavefront_compute_edit.h" +#include "wavefront_compute_linear.h" +#include "wavefront_compute_affine.h" +#include "wavefront_compute_affine2p.h" +#include "wavefront_extend.h" +#include "wavefront_backtrace.h" +#include "wavefront_debug.h" + +/* + * Checks + */ +void wavefront_align_checks( + wavefront_aligner_t* const wf_aligner, + const int pattern_length, + const int text_length) { + alignment_form_t* const form = &wf_aligner->alignment_form; + if (wf_aligner->bialigner != NULL) { + const bool ends_free = + form->pattern_begin_free > 0 || + form->pattern_end_free > 0 || + form->text_begin_free > 0 || + form->text_end_free > 0; + if (ends_free) { + fprintf(stderr,"[WFA] BiWFA and ends-free is not supported yet\n"); + exit(1); + } + } + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + const bool is_heuristic_drop = + (wf_aligner->heuristic.strategy & wf_heuristic_xdrop) || + (wf_aligner->heuristic.strategy & wf_heuristic_zdrop); + if (is_heuristic_drop && (distance_metric==edit || distance_metric==indel)) { + fprintf(stderr,"[WFA] Heuristics drops are not compatible with 'edit'/'indel' distance metrics\n"); + exit(1); + } + if (form->span == alignment_endsfree) { + if (form->pattern_begin_free > pattern_length || + form->pattern_end_free > pattern_length || + form->text_begin_free > text_length || + form->text_end_free > text_length) { + fprintf(stderr,"[WFA] Ends-free parameters must be not larger than the sequences " + "(P0=%d,Pf=%d,T0=%d,Tf=%d). Must be (P0<=|P|,Pf<=|P|,T0<=|T|,Tf<=|T|) where (|P|,|T|)=(%d,%d)\n", + form->pattern_begin_free,form->pattern_end_free, + form->text_begin_free,form->text_end_free, + pattern_length,text_length); + exit(1); + } + } +} +/* + * Wavefront Alignment Unidirectional + */ +void wavefront_align_unidirectional_cleanup( + wavefront_aligner_t* const wf_aligner) { + // Compute memory used + uint64_t memory_used = wavefront_aligner_get_size(wf_aligner); + wf_aligner->align_status.memory_used = memory_used; + // Reap memory (controlled reaping) + if (memory_used > wf_aligner->system.max_memory_resident) { + // Wavefront components + wavefront_components_reap(&wf_aligner->wf_components); + // Check memory + memory_used = wavefront_aligner_get_size(wf_aligner); + wf_aligner->align_status.memory_used = memory_used; + // Slab + if (memory_used > wf_aligner->system.max_memory_resident) { + wavefront_slab_reap(wf_aligner->wavefront_slab); + if (wf_aligner->bialigner != NULL) { + wavefront_bialigner_reap(wf_aligner->bialigner); + } + } + } +} +void wavefront_align_unidirectional( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length) { + // Prepare alignment + wavefront_unialign_init( + wf_aligner,pattern,pattern_length,text,text_length, + affine2p_matrix_M,affine2p_matrix_M); + // DEBUG + wavefront_debug_prologue(wf_aligner,pattern,pattern_length,text,text_length); + // Wavefront align sequences + wavefront_unialign(wf_aligner); + // Finish + if (wf_aligner->align_status.status == WF_STATUS_MAX_SCORE_REACHED) return; // Alignment paused + wavefront_align_unidirectional_cleanup(wf_aligner); + // DEBUG + wavefront_debug_epilogue(wf_aligner); + wavefront_debug_check_correct(wf_aligner); +} +/* + * Wavefront Alignment Bidirectional + */ +void wavefront_align_bidirectional( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length) { + // DEBUG + wavefront_debug_prologue(wf_aligner,pattern,pattern_length,text,text_length); + // Bidirectional alignment + wavefront_bialign(wf_aligner,pattern,pattern_length,text,text_length); + // Finish + const uint64_t memory_used = wavefront_aligner_get_size(wf_aligner); + wf_aligner->align_status.memory_used = memory_used; + // DEBUG + wavefront_debug_epilogue(wf_aligner); + wavefront_debug_check_correct(wf_aligner); +} +/* + * Wavefront Alignment Dispatcher + */ +int wavefront_align( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length) { + // Checks + wavefront_align_checks(wf_aligner,pattern_length,text_length); + // Plot + if (wf_aligner->plot != NULL) { + wavefront_plot_resize(wf_aligner->plot,pattern_length,text_length); + } + // Dispatcher + if (wf_aligner->bialigner != NULL) { + wavefront_align_bidirectional(wf_aligner,pattern,pattern_length,text,text_length); + } else { + wavefront_align_unidirectional(wf_aligner,pattern,pattern_length,text,text_length); + } + // Return + return wf_aligner->align_status.status; +} +int wavefront_align_resume( + wavefront_aligner_t* const wf_aligner) { + // Parameters + wavefront_align_status_t* const align_status = &wf_aligner->align_status; + // Check current alignment status + if (align_status->status != WF_STATUS_MAX_SCORE_REACHED || + wf_aligner->bialigner != NULL) { + fprintf(stderr,"[WFA] Alignment cannot be resumed\n"); + exit(1); + } + // Resume aligning sequences + wavefront_unialign(wf_aligner); + // Finish alignment + if (align_status->status == WF_STATUS_MAX_SCORE_REACHED) { + return WF_STATUS_MAX_SCORE_REACHED; // Alignment paused + } + wavefront_align_unidirectional_cleanup(wf_aligner); + // DEBUG + wavefront_debug_epilogue(wf_aligner); + wavefront_debug_check_correct(wf_aligner); + // Return + return align_status->status; +} + diff --git a/src/lib/wfa2/wavefront/wavefront_align.h b/src/lib/wfa2/wavefront/wavefront_align.h new file mode 100644 index 000000000..7dd841c6e --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_align.h @@ -0,0 +1,49 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for sequence pairwise alignment + */ + +#ifndef WAVEFRONT_ALIGN_H_ +#define WAVEFRONT_ALIGN_H_ + +#include "wavefront_aligner.h" + +/* + * Wavefront Alignment + */ +int wavefront_align( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length); +int wavefront_align_resume( + wavefront_aligner_t* const wf_aligner); + +#endif /* WAVEFRONT_ALIGN_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_aligner.c b/src/lib/wfa2/wavefront/wavefront_aligner.c new file mode 100644 index 000000000..906b350b0 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_aligner.c @@ -0,0 +1,446 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront aligner data structure + */ + +#include "wavefront_aligner.h" +#include "wavefront_components.h" +#include "wavefront_heuristic.h" +#include "wavefront_plot.h" + +/* + * Configuration + */ +#define PATTERN_LENGTH_INIT 1000 +#define TEXT_LENGTH_INIT 1000 + +/* + * Error messages + */ +char* wf_error_msg[] = +{ + /* WF_STATUS_OOM == -3 */ "[WFA] Alignment failed. Maximum memory threshold reached", + /* WF_STATUS_MAX_SCORE_REACHED == -2 */ "[WFA] Alignment failed. Maximum score reached", + /* WF_STATUS_UNFEASIBLE == -1 */ "[WFA] Alignment unfeasible (possible due to heuristic parameters)", + /* WF_STATUS_SUCCESSFUL == 0 */ "[WFA] Alignment finished successfully", +}; +char* wavefront_align_strerror(const int error_code) { + if (error_code > 0) { + fprintf(stderr,"[WFA] Internal alignment error code (%d)",error_code); + exit(1); + } + return wf_error_msg[error_code+3]; +} +/* + * Setup + */ +wavefront_aligner_t* wavefront_aligner_init_mm( + mm_allocator_t* mm_allocator, + const bool memory_modular, + const bool bt_piggyback, + const bool bi_alignment) { + // MM + bool mm_allocator_own; + if (mm_allocator == NULL) { + mm_allocator = mm_allocator_new((bi_alignment) ? BUFFER_SIZE_4K : BUFFER_SIZE_4M); + mm_allocator_own = true; + } else { + mm_allocator_own = false; + } + // Handler + wavefront_aligner_t* const wf_aligner = + mm_allocator_alloc(mm_allocator,wavefront_aligner_t); + // Configure MM + wf_aligner->mm_allocator = mm_allocator; + wf_aligner->mm_allocator_own = mm_allocator_own; + // Slab + if (bi_alignment) { + wf_aligner->wavefront_slab = NULL; + } else { + const wf_slab_mode_t slab_mode = (memory_modular) ? wf_slab_reuse : wf_slab_tight; + wf_aligner->wavefront_slab = wavefront_slab_new(1000,bt_piggyback,slab_mode,wf_aligner->mm_allocator); + } + // Return + return wf_aligner; +} +void wavefront_aligner_init_penalties( + wavefront_aligner_t* const wf_aligner, + wavefront_aligner_attr_t* const attributes) { + switch (attributes->distance_metric) { + case indel: + wavefront_penalties_set_indel(&wf_aligner->penalties); + break; + case edit: + wavefront_penalties_set_edit(&wf_aligner->penalties); + break; + case gap_linear: + wavefront_penalties_set_linear( + &wf_aligner->penalties, + &attributes->linear_penalties); + break; + case gap_affine: + wavefront_penalties_set_affine( + &wf_aligner->penalties, + &attributes->affine_penalties); + break; + case gap_affine_2p: + wavefront_penalties_set_affine2p( + &wf_aligner->penalties, + &attributes->affine2p_penalties); + break; + } +} +void wavefront_aligner_init_heuristic( + wavefront_aligner_t* const wf_aligner, + wavefront_aligner_attr_t* const attributes) { + // Parameters + wavefront_heuristic_t* const wf_heuristic = &attributes->heuristic; + // Select and configure heuristics + if (wf_heuristic->strategy == wf_heuristic_none) { + wavefront_heuristic_set_none(&wf_aligner->heuristic); + } else { + // WF-Adaptive + if (wf_heuristic->strategy & wf_heuristic_wfadaptive) { + wavefront_heuristic_set_wfadaptive( + &wf_aligner->heuristic,wf_heuristic->min_wavefront_length, + wf_heuristic->max_distance_threshold,wf_heuristic->steps_between_cutoffs); + } else if (wf_heuristic->strategy & wf_heuristic_wfmash) { + wavefront_heuristic_set_wfmash( + &wf_aligner->heuristic,wf_heuristic->min_wavefront_length, + wf_heuristic->max_distance_threshold,wf_heuristic->steps_between_cutoffs); + } + // Drops + if (wf_heuristic->strategy & wf_heuristic_xdrop) { + wavefront_heuristic_set_xdrop(&wf_aligner->heuristic, + wf_heuristic->xdrop,wf_heuristic->steps_between_cutoffs); + } else if (wf_heuristic->strategy & wf_heuristic_zdrop) { + wavefront_heuristic_set_zdrop(&wf_aligner->heuristic, + wf_heuristic->zdrop,wf_heuristic->steps_between_cutoffs); + } + // Banded + if (wf_heuristic->strategy & wf_heuristic_banded_static) { + wavefront_heuristic_set_banded_static(&wf_aligner->heuristic, + wf_heuristic->min_k,wf_heuristic->max_k); + } else if (wf_heuristic->strategy & wf_heuristic_banded_adaptive) { + wavefront_heuristic_set_banded_adaptive(&wf_aligner->heuristic, + wf_heuristic->min_k,wf_heuristic->max_k,wf_heuristic->steps_between_cutoffs); + } + } +} +void wavefront_aligner_init_alignment( + wavefront_aligner_t* const wf_aligner, + wavefront_aligner_attr_t* const attributes, + const bool memory_modular, + const bool bt_piggyback, + const bool bi_alignment) { + // Mode + wf_aligner->align_mode = (bi_alignment) ? wf_align_biwfa : wf_align_regular; + wf_aligner->align_mode_tag = NULL; + // Score & form + wf_aligner->alignment_scope = attributes->alignment_scope; + wf_aligner->alignment_form = attributes->alignment_form; + // Penalties + wavefront_aligner_init_penalties(wf_aligner,attributes); + // Memory mode + wf_aligner->memory_mode = attributes->memory_mode; + wavefront_aligner_init_heuristic(wf_aligner,attributes); + // Custom matching functions + wf_aligner->match_funct = attributes->match_funct; + wf_aligner->match_funct_arguments = attributes->match_funct_arguments; +} +wavefront_aligner_t* wavefront_aligner_new( + wavefront_aligner_attr_t* attributes) { + // Parameters + if (attributes == NULL) attributes = &wavefront_aligner_attr_default; + const bool score_only = (attributes->alignment_scope == compute_score); + const bool memory_succint = + attributes->memory_mode == wavefront_memory_med || + attributes->memory_mode == wavefront_memory_low; + const bool memory_modular = score_only || memory_succint; + const bool bt_piggyback = !score_only && memory_succint; + const bool bi_alignment = (attributes->memory_mode == wavefront_memory_ultralow); + // Handler + wavefront_aligner_t* const wf_aligner = wavefront_aligner_init_mm( + attributes->mm_allocator,memory_modular,bt_piggyback,bi_alignment); + // Plot + if (attributes->plot.enabled) { + wf_aligner->plot = wavefront_plot_new(attributes->distance_metric, + PATTERN_LENGTH_INIT,TEXT_LENGTH_INIT,&attributes->plot); + } else { + wf_aligner->plot = NULL; + } + // Alignment + wavefront_aligner_init_alignment(wf_aligner,attributes,memory_modular,bt_piggyback,bi_alignment); + if (bi_alignment) { + wf_aligner->bialigner = wavefront_bialigner_new(attributes,wf_aligner->plot); + } else { + wf_aligner->bialigner = NULL; + // Wavefront components + wavefront_components_allocate( + &wf_aligner->wf_components,PATTERN_LENGTH_INIT,TEXT_LENGTH_INIT, + &wf_aligner->penalties,memory_modular,bt_piggyback, + wf_aligner->mm_allocator); + } + // Sequences + wf_aligner->sequences = NULL; + // CIGAR + const int cigar_length = (score_only) ? 10 : 2*(PATTERN_LENGTH_INIT+TEXT_LENGTH_INIT); + wf_aligner->cigar = cigar_new(cigar_length,wf_aligner->mm_allocator); + // System + wf_aligner->system = attributes->system; + // Return + return wf_aligner; +} +void wavefront_aligner_reap( + wavefront_aligner_t* const wf_aligner) { + // Padded sequences + if (wf_aligner->sequences != NULL) { + strings_padded_delete(wf_aligner->sequences); + wf_aligner->sequences = NULL; + } + // Select alignment mode + if (wf_aligner->bialigner != NULL) { + wavefront_bialigner_reap(wf_aligner->bialigner); + } else { + // Wavefront components + wavefront_components_reap(&wf_aligner->wf_components); + // Slab + wavefront_slab_reap(wf_aligner->wavefront_slab); + } +} +void wavefront_aligner_delete( + wavefront_aligner_t* const wf_aligner) { + // Parameters + mm_allocator_t* const mm_allocator = wf_aligner->mm_allocator; + const bool mm_allocator_own = wf_aligner->mm_allocator_own; + // Padded sequences + if (wf_aligner->sequences != NULL) { + strings_padded_delete(wf_aligner->sequences); + } + // Select alignment mode + if (wf_aligner->bialigner != NULL) { + wavefront_bialigner_delete(wf_aligner->bialigner); + } else { + // Wavefront components + wavefront_components_free(&wf_aligner->wf_components); + // Slab + wavefront_slab_delete(wf_aligner->wavefront_slab); + } + // CIGAR + cigar_free(wf_aligner->cigar); + // Plot + if (wf_aligner->plot != NULL && wf_aligner->align_mode <= 1) { + wavefront_plot_delete(wf_aligner->plot); + } + // MM + mm_allocator_free(mm_allocator,wf_aligner); + if (mm_allocator_own) { + mm_allocator_delete(wf_aligner->mm_allocator); + } +} +/* + * Span configuration + */ +void wavefront_aligner_set_alignment_end_to_end( + wavefront_aligner_t* const wf_aligner) { + wf_aligner->alignment_form.span = alignment_end2end; +} +void wavefront_aligner_set_alignment_free_ends( + wavefront_aligner_t* const wf_aligner, + const int pattern_begin_free, + const int pattern_end_free, + const int text_begin_free, + const int text_end_free) { + wf_aligner->alignment_form.span = alignment_endsfree; + wf_aligner->alignment_form.pattern_begin_free = pattern_begin_free; + wf_aligner->alignment_form.pattern_end_free = pattern_end_free; + wf_aligner->alignment_form.text_begin_free = text_begin_free; + wf_aligner->alignment_form.text_end_free = text_end_free; +} +/* + * Heuristic configuration + */ +void wavefront_aligner_set_heuristic_none( + wavefront_aligner_t* const wf_aligner) { + wavefront_heuristic_set_none(&wf_aligner->heuristic); + if (wf_aligner->bialigner != NULL) { + wavefront_bialigner_heuristic_inherit(wf_aligner->bialigner,&wf_aligner->heuristic); + } +} +void wavefront_aligner_set_heuristic_banded_static( + wavefront_aligner_t* const wf_aligner, + const int band_min_k, + const int band_max_k) { + wavefront_heuristic_set_banded_static(&wf_aligner->heuristic,band_min_k,band_max_k); + if (wf_aligner->bialigner != NULL) { + wavefront_bialigner_heuristic_inherit(wf_aligner->bialigner,&wf_aligner->heuristic); + } +} +void wavefront_aligner_set_heuristic_banded_adaptive( + wavefront_aligner_t* const wf_aligner, + const int band_min_k, + const int band_max_k, + const int score_steps) { + wavefront_heuristic_set_banded_adaptive( + &wf_aligner->heuristic,band_min_k,band_max_k,score_steps); + if (wf_aligner->bialigner != NULL) { + wavefront_bialigner_heuristic_inherit(wf_aligner->bialigner,&wf_aligner->heuristic); + } +} +void wavefront_aligner_set_heuristic_wfadaptive( + wavefront_aligner_t* const wf_aligner, + const int min_wavefront_length, + const int max_distance_threshold, + const int score_steps) { + wavefront_heuristic_set_wfadaptive( + &wf_aligner->heuristic, + min_wavefront_length,max_distance_threshold,score_steps); + if (wf_aligner->bialigner != NULL) { + wavefront_bialigner_heuristic_inherit(wf_aligner->bialigner,&wf_aligner->heuristic); + } +} +void wavefront_aligner_set_heuristic_xdrop( + wavefront_aligner_t* const wf_aligner, + const int xdrop, + const int score_steps) { + wavefront_heuristic_set_xdrop(&wf_aligner->heuristic,xdrop,score_steps); + if (wf_aligner->bialigner != NULL) { + wavefront_bialigner_heuristic_inherit(wf_aligner->bialigner,&wf_aligner->heuristic); + } +} +void wavefront_aligner_set_heuristic_zdrop( + wavefront_aligner_t* const wf_aligner, + const int ydrop, + const int score_steps) { + wavefront_heuristic_set_zdrop(&wf_aligner->heuristic,ydrop,score_steps); + if (wf_aligner->bialigner != NULL) { + wavefront_bialigner_heuristic_inherit(wf_aligner->bialigner,&wf_aligner->heuristic); + } +} +/* + * Match-funct configuration + */ +void wavefront_aligner_set_match_funct( + wavefront_aligner_t* const wf_aligner, + int (*match_funct)(int,int,void*), + void* const match_funct_arguments) { + wf_aligner->match_funct = match_funct; + wf_aligner->match_funct_arguments = match_funct_arguments; +} +/* + * System configuration + */ +void wavefront_aligner_set_max_alignment_score( + wavefront_aligner_t* const wf_aligner, + const int max_alignment_score) { + wf_aligner->system.max_alignment_score = max_alignment_score; +} +void wavefront_aligner_set_max_memory( + wavefront_aligner_t* const wf_aligner, + const uint64_t max_memory_resident, + const uint64_t max_memory_abort) { + wf_aligner->system.max_memory_resident = max_memory_resident; + wf_aligner->system.max_memory_abort = max_memory_abort; +} +/* + * Utils + */ +uint64_t wavefront_aligner_get_size( + wavefront_aligner_t* const wf_aligner) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + // Bialigner + uint64_t sub_aligners = 0; + if (wf_aligner->bialigner != NULL) { + return wavefront_bialigner_get_size(wf_aligner->bialigner); + } else { + // Compute aligner size + const uint64_t bt_buffer_size = (wf_components->bt_buffer) ? + wf_backtrace_buffer_get_size_allocated(wf_components->bt_buffer) : 0; + const uint64_t slab_size = wavefront_slab_get_size(wf_aligner->wavefront_slab); + // Return overall size + return sub_aligners + bt_buffer_size + slab_size; + } +} +/* + * Display + */ +void wavefront_aligner_print_type( + FILE* const stream, + wavefront_aligner_t* const wf_aligner) { + if (wf_aligner->align_mode_tag == NULL) { + switch (wf_aligner->align_mode) { + case wf_align_biwfa: + fprintf(stream,"BiWFA"); + break; + case wf_align_biwfa_breakpoint_forward: + fprintf(stream,"BiWFA::Forward"); + break; + case wf_align_biwfa_breakpoint_reverse: + fprintf(stream,"BiWFA::Reverse"); + break; + case wf_align_biwfa_subsidiary: + fprintf(stream,"BiWFA::SubWFA"); + break; + default: + fprintf(stream,"WFA"); + break; + } + } else { + fprintf(stream,"%s",wf_aligner->align_mode_tag); + } +} +void wavefront_aligner_print_scope( + FILE* const stream, + wavefront_aligner_t* const wf_aligner) { + const char* const scope_label = + (wf_aligner->alignment_scope == compute_score) ? "score" : "alignment"; + if (wf_aligner->alignment_form.span == alignment_end2end) { + fprintf(stream,"(%s,end2end)",scope_label); + } else { + fprintf(stream,"(%s,endsfree,%d,%d,%d,%d)", + scope_label, + wf_aligner->alignment_form.pattern_begin_free, + wf_aligner->alignment_form.pattern_end_free, + wf_aligner->alignment_form.text_begin_free, + wf_aligner->alignment_form.text_end_free); + } +} +void wavefront_aligner_print_mode( + FILE* const stream, + wavefront_aligner_t* const wf_aligner) { + fprintf(stream,"(%s,",(wf_aligner->alignment_scope==compute_score)?"Score":"Alg"); + switch (wf_aligner->memory_mode) { + case wavefront_memory_high: fprintf(stream,"MHigh)"); break; + case wavefront_memory_med: fprintf(stream,"MMed)"); break; + case wavefront_memory_low: fprintf(stream,"MLow)"); break; + case wavefront_memory_ultralow: fprintf(stream,"BiWFA)"); break; + } +} + diff --git a/src/lib/wfa2/wavefront/wavefront_aligner.h b/src/lib/wfa2/wavefront/wavefront_aligner.h new file mode 100644 index 000000000..574337c84 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_aligner.h @@ -0,0 +1,220 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront aligner data structure + */ + +#ifndef WAVEFRONT_ALIGNER_H_ +#define WAVEFRONT_ALIGNER_H_ + +#include "../utils/commons.h" +#include "../utils/heatmap.h" +#include "../utils/string_padded.h" +#include "../system/profiler_counter.h" +#include "../system/profiler_timer.h" +#include "../system/mm_allocator.h" +#include "../system/mm_stack.h" +#include "../alignment/cigar.h" +#include "wavefront_slab.h" +#include "wavefront_penalties.h" +#include "wavefront_attributes.h" +#include "wavefront_components.h" +#include "wavefront_bialigner.h" + +/* + * Error codes & messages + */ +// Success +#define WF_STATUS_SUCCESSFUL 0 +// Errors +#define WF_STATUS_UNFEASIBLE -1 +#define WF_STATUS_MAX_SCORE_REACHED -2 +#define WF_STATUS_OOM -3 +// Internal +#define WF_STATUS_END_REACHED 1 +// Error messages +extern char* wf_error_msg[5]; +char* wavefront_align_strerror(const int error_code); + +/* + * Alignment status + */ +typedef struct _wavefront_aligner_t wavefront_aligner_t; +typedef struct { + // Status + int status; // Status code + int score; // Current WF-alignment score + int num_null_steps; // Total contiguous null-steps performed + uint64_t memory_used; // Total memory used + // Wavefront alignment functions + void (*wf_align_compute)(wavefront_aligner_t* const,const int); // WF Compute function + int (*wf_align_extend)(wavefront_aligner_t* const,const int); // WF Extend function +} wavefront_align_status_t; + +/* + * Alignment type + */ +typedef enum { + wf_align_regular = 0, + wf_align_biwfa = 1, + wf_align_biwfa_breakpoint_forward = 2, + wf_align_biwfa_breakpoint_reverse = 3, + wf_align_biwfa_subsidiary = 4 +} wavefront_align_mode_t; + +/* + * Wavefront Aligner + */ +typedef struct _wavefront_aligner_t { + // Mode and status + wavefront_align_mode_t align_mode; // WFA alignment mode + char* align_mode_tag; // WFA mode tag + wavefront_align_status_t align_status; // Current alignment status + // Sequences + strings_padded_t* sequences; // Padded sequences + char* pattern; // Pattern sequence (padded) + int pattern_length; // Pattern length + char* text; // Text sequence (padded) + int text_length; // Text length + // Custom function to compare sequences + alignment_match_funct_t match_funct; // Custom matching function (match(v,h,args)) + void* match_funct_arguments; // Generic arguments passed to matching function (args) + // Alignment Attributes + alignment_scope_t alignment_scope; // Alignment scope (score only or full-CIGAR) + alignment_form_t alignment_form; // Alignment form (end-to-end/ends-free) + wavefront_penalties_t penalties; // Alignment penalties + wavefront_heuristic_t heuristic; // Heuristic's parameters + wavefront_memory_t memory_mode; // Wavefront memory strategy (modular wavefronts and piggyback) + // Wavefront components + wavefront_components_t wf_components; // Wavefront components + affine2p_matrix_type component_begin; // Alignment begin component + affine2p_matrix_type component_end; // Alignment end component + wavefront_pos_t alignment_end_pos; // Alignment end position + // Bidirectional Alignment + wavefront_bialigner_t* bialigner; // BiWFA aligner + // CIGAR + cigar_t* cigar; // Alignment CIGAR + // MM + bool mm_allocator_own; // Ownership of MM-Allocator + mm_allocator_t* mm_allocator; // MM-Allocator + wavefront_slab_t* wavefront_slab; // MM-Wavefront-Slab (Allocates/Reuses the individual wavefronts) + // Display + wavefront_plot_t* plot; // Wavefront plot + // System + alignment_system_t system; // System related parameters +} wavefront_aligner_t; + +/* + * Setup + */ +wavefront_aligner_t* wavefront_aligner_new( + wavefront_aligner_attr_t* attributes); +void wavefront_aligner_reap( + wavefront_aligner_t* const wf_aligner); +void wavefront_aligner_delete( + wavefront_aligner_t* const wf_aligner); + +/* + * Span configuration + */ +void wavefront_aligner_set_alignment_end_to_end( + wavefront_aligner_t* const wf_aligner); +void wavefront_aligner_set_alignment_free_ends( + wavefront_aligner_t* const wf_aligner, + const int pattern_begin_free, + const int pattern_end_free, + const int text_begin_free, + const int text_end_free); + +/* + * Heuristic configuration + */ +void wavefront_aligner_set_heuristic_none( + wavefront_aligner_t* const wf_aligner); +void wavefront_aligner_set_heuristic_wfadaptive( + wavefront_aligner_t* const wf_aligner, + const int min_wavefront_length, + const int max_distance_threshold, + const int score_steps); +void wavefront_aligner_set_heuristic_xdrop( + wavefront_aligner_t* const wf_aligner, + const int xdrop, + const int score_steps); +void wavefront_aligner_set_heuristic_zdrop( + wavefront_aligner_t* const wf_aligner, + const int ydrop, + const int score_steps); +void wavefront_aligner_set_heuristic_banded_static( + wavefront_aligner_t* const wf_aligner, + const int band_min_k, + const int band_max_k); +void wavefront_aligner_set_heuristic_banded_adaptive( + wavefront_aligner_t* const wf_aligner, + const int band_min_k, + const int band_max_k, + const int score_steps); + +/* + * Match-funct configuration + */ +void wavefront_aligner_set_match_funct( + wavefront_aligner_t* const wf_aligner, + int (*match_funct)(int,int,void*), + void* const match_funct_arguments); + +/* + * System configuration + */ +void wavefront_aligner_set_max_alignment_score( + wavefront_aligner_t* const wf_aligner, + const int max_alignment_score); +void wavefront_aligner_set_max_memory( + wavefront_aligner_t* const wf_aligner, + const uint64_t max_memory_resident, + const uint64_t max_memory_abort); + +/* + * Utils + */ +uint64_t wavefront_aligner_get_size( + wavefront_aligner_t* const wf_aligner); + +/* + * Display + */ +void wavefront_aligner_print_type( + FILE* const stream, + wavefront_aligner_t* const wf_aligner); +void wavefront_aligner_print_scope( + FILE* const stream, + wavefront_aligner_t* const wf_aligner); +void wavefront_aligner_print_mode( + FILE* const stream, + wavefront_aligner_t* const wf_aligner); + +#endif /* WAVEFRONT_ALIGNER_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_attributes.c b/src/lib/wfa2/wavefront/wavefront_attributes.c new file mode 100644 index 000000000..cafb93db9 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_attributes.c @@ -0,0 +1,101 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront aligner data structure attributes + */ + +#include "wavefront_attributes.h" + +/* + * Default parameters + */ +wavefront_aligner_attr_t wavefront_aligner_attr_default = { + // Distance model & Penalties + .distance_metric = gap_affine, + .alignment_scope = compute_alignment, + .alignment_form = { + .span = alignment_end2end, + .pattern_begin_free = 0, + .pattern_end_free = 0, + .text_begin_free = 0, + .text_end_free = 0, + }, + // Custom matching functions + .match_funct = NULL, // Use default match-compare function + .match_funct_arguments = NULL, // No arguments + // Penalties + .linear_penalties = { + .match = 0, + .mismatch = 4, + .indel = 2, + }, + .affine_penalties = { + .match = 0, + .mismatch = 4, + .gap_opening = 6, + .gap_extension = 2, + }, + .affine2p_penalties = { + .match = 0, + .mismatch = 4, + .gap_opening1 = 6, + .gap_extension1 = 2, + .gap_opening2 = 24, + .gap_extension2 = 1, + }, + // Heuristic + .heuristic = { + .strategy = wf_heuristic_wfadaptive, + .min_wavefront_length = 10, + .max_distance_threshold = 50, + .steps_between_cutoffs = 1, + }, + // Memory model + .memory_mode = wavefront_memory_high, + // MM + .mm_allocator = NULL, // Use private MM + // Display + .plot = { + .enabled = false, + .resolution_points = 2000, + .align_level = 0, + }, + // System + .system = { + .max_alignment_score = INT_MAX, // Unlimited + .probe_interval_global = 3000, + .probe_interval_compact = 6000, + .max_memory_compact = -1, // Automatically set based on memory-mode + .max_memory_resident = -1, // Automatically set based on memory-mode + .max_memory_abort = UINT64_MAX, // Unlimited + .verbose = 0, // Quiet + .check_alignment_correct = false, + .max_num_threads = 1, // Single thread by default + .min_offsets_per_thread = 500 // Minimum WF-length to spawn a thread + }, +}; diff --git a/src/lib/wfa2/wavefront/wavefront_attributes.h b/src/lib/wfa2/wavefront/wavefront_attributes.h new file mode 100644 index 000000000..622236051 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_attributes.h @@ -0,0 +1,161 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront aligner data structure attributes + */ + +#ifndef WAVEFRONT_ATTRIBUTES_H_ +#define WAVEFRONT_ATTRIBUTES_H_ + +#include "../utils/commons.h" +#include "../alignment/cigar.h" +#include "../alignment/affine_penalties.h" +#include "../alignment/affine2p_penalties.h" +#include "../alignment/linear_penalties.h" +#include "../system/profiler_timer.h" +#include "../system/mm_allocator.h" + +#include "wavefront_penalties.h" +#include "wavefront_plot.h" +#include "wavefront_display.h" +#include "wavefront_heuristic.h" + +/* + * Alignment scope + */ +typedef enum { + compute_score, // Only distance/score + compute_alignment, // Full alignment CIGAR +} alignment_scope_t; +typedef enum { + alignment_end2end, // End-to-end alignment (aka global) + alignment_endsfree, // Ends-free alignment (semiglobal, glocal, etc) +} alignment_span_t; +typedef struct { + // Mode + alignment_span_t span; // Alignment form (End-to-end/Ends-free) + // Ends-free + int pattern_begin_free; // Allow free-gap at the beginning of the pattern + int pattern_end_free; // Allow free-gap at the end of the pattern + int text_begin_free; // Allow free-gap at the beginning of the text + int text_end_free; // Allow free-gap at the end of the text +} alignment_form_t; + +/* + * Custom extend-match function, e.g.: + * + * typedef struct { + * char* pattern; + * int pattern_length; + * char* text; + * int text_length; + * } match_function_params_t; + * + * int match_function(int v,int h,void* arguments) { + * // Extract parameters + * match_function_params_t* match_arguments = (match_function_params_t*)arguments; + * // Check match + * if (v > match_arguments->pattern_length || h > match_arguments->text_length) return 0; + * return (match_arguments->pattern[v] == match_arguments->text[h]); + * } + */ +typedef int (*alignment_match_funct_t)(int,int,void*); + +/* + * Alignment system configuration + */ +typedef struct { + // Limits + int max_alignment_score; // Maximum score allowed before quit + // Probing intervals + int probe_interval_global; // Score-ticks interval to check any limits + int probe_interval_compact; // Score-ticks interval to check BT-buffer compacting + // Memory + uint64_t max_partial_compacts; // Maximum partial-compacts before attempting full-compact + uint64_t max_memory_compact; // Maximum BT-buffer memory allowed before trigger compact + uint64_t max_memory_resident; // Maximum memory allowed to be buffered before reap + uint64_t max_memory_abort; // Maximum memory allowed to be used before aborting alignment + // Verbose + // 0 - Quiet + // 1 - Report each sequence aligned (brief) + // 2 - Report each sequence/subsequence aligned (brief) + // 3 - Report WFA progress (heavy tasks) (verbose) + // 4 - Full report of each sequence/subsequence aligned (very verbose) + int verbose; // Verbose (regulates messages during alignment) + // Debug + bool check_alignment_correct; // Verify that the alignment CIGAR output is correct + // Profile + profiler_timer_t timer; // Time alignment + // OS + int max_num_threads; // Maximum number of threads to use to compute/extend WFs + int min_offsets_per_thread; // Minimum amount of offsets to spawn a thread +} alignment_system_t; + +/* + * Low-memory modes + */ +typedef enum { + wavefront_memory_high = 0, // High-memore mode (fastest, stores all WFs explicitly) + wavefront_memory_med = 1, // Succing-memory mode piggyback-based (medium, offloads half-full BT-blocks) + wavefront_memory_low = 2, // Succing-memory mode piggyback-based (slow, offloads only full BT-blocks) + wavefront_memory_ultralow = 3, // Bidirectional WFA +} wavefront_memory_t; + +/* + * Wavefront Aligner Attributes + */ +typedef struct { + // Distance model + distance_metric_t distance_metric; // Alignment metric/distance used + alignment_scope_t alignment_scope; // Alignment scope (score only or full-CIGAR) + alignment_form_t alignment_form; // Alignment mode (end-to-end/ends-free) + // Penalties + linear_penalties_t linear_penalties; // Gap-linear penalties (placeholder) + affine_penalties_t affine_penalties; // Gap-affine penalties (placeholder) + affine2p_penalties_t affine2p_penalties; // Gap-affine-2p penalties (placeholder) + // Heuristic strategy + wavefront_heuristic_t heuristic; // Wavefront heuristic + // Memory model + wavefront_memory_t memory_mode; // Wavefront memory strategy (modular wavefronts and piggyback) + // Custom function to compare sequences + alignment_match_funct_t match_funct; // Custom matching function (match(v,h,args)) + void* match_funct_arguments; // Generic arguments passed to matching function (args) + // External MM (instead of allocating one inside) + mm_allocator_t* mm_allocator; // MM-Allocator + // Display + wavefront_plot_attr_t plot; // Plot wavefront + // System + alignment_system_t system; // System related parameters +} wavefront_aligner_attr_t; + +/* + * Default parameters + */ +extern wavefront_aligner_attr_t wavefront_aligner_attr_default; + +#endif /* WAVEFRONT_ATTRIBUTES_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_backtrace.c b/src/lib/wfa2/wavefront/wavefront_backtrace.c new file mode 100644 index 000000000..3be9a0ab9 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_backtrace.c @@ -0,0 +1,566 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront-Alignment module for backtracing alignments + */ + +#include "wavefront_backtrace.h" + +/* + * Wavefront type + */ +#define BACKTRACE_TYPE_BITS 4 // 4-bits for piggyback +#define BACKTRACE_TYPE_MASK 0x000000000000000Fl // Extract mask + +#define BACKTRACE_PIGGYBACK_SET(offset,backtrace_type) \ + (( ((int64_t)(offset)) << BACKTRACE_TYPE_BITS) | backtrace_type) + +#define BACKTRACE_PIGGYBACK_GET_TYPE(offset) \ + ((offset) & BACKTRACE_TYPE_MASK) +#define BACKTRACE_PIGGYBACK_GET_OFFSET(offset) \ + ((offset) >> BACKTRACE_TYPE_BITS) + +typedef enum { + backtrace_M = 9, + backtrace_D2_ext = 8, + backtrace_D2_open = 7, + backtrace_D1_ext = 6, + backtrace_D1_open = 5, + backtrace_I2_ext = 4, + backtrace_I2_open = 3, + backtrace_I1_ext = 2, + backtrace_I1_open = 1, +} backtrace_type; + +/* + * Backtrace Trace Patch Match/Mismsmatch + */ +int64_t wavefront_backtrace_misms( + wavefront_aligner_t* const wf_aligner, + const int score, + const int k) { + if (score < 0) return WAVEFRONT_OFFSET_NULL; + wavefront_t* const mwavefront = wf_aligner->wf_components.mwavefronts[score]; + if (mwavefront != NULL && + mwavefront->lo <= k && + k <= mwavefront->hi) { + return BACKTRACE_PIGGYBACK_SET(mwavefront->offsets[k]+1,backtrace_M); + } else { + return WAVEFRONT_OFFSET_NULL; + } +} +void wavefront_backtrace_matches( + wavefront_aligner_t* const wf_aligner, + const int k, + wf_offset_t offset, + int num_matches, + cigar_t* const cigar) { + // Parameters + const uint64_t matches_lut = 0x4D4D4D4D4D4D4D4Dul; // Matches LUT = "MMMMMMMM" + char* operations = cigar->operations + cigar->begin_offset; + // Update offset first + cigar->begin_offset -= num_matches; + // Blocks of 8-matches + while (num_matches >= 8) { + operations -= 8; + *((uint64_t*)(operations+1)) = matches_lut; + num_matches -= 8; + } + // Remaining matches + int i; + for (i=0;iwf_components.mwavefronts[score]; + if (mwavefront != NULL && + mwavefront->lo <= k+1 && + k+1 <= mwavefront->hi) { + return BACKTRACE_PIGGYBACK_SET(mwavefront->offsets[k+1],backtrace_D1_open); + } else { + return WAVEFRONT_OFFSET_NULL; + } +} +int64_t wavefront_backtrace_del2_open( + wavefront_aligner_t* const wf_aligner, + const int score, + const int k) { + if (score < 0) return WAVEFRONT_OFFSET_NULL; + wavefront_t* const mwavefront = wf_aligner->wf_components.mwavefronts[score]; + if (mwavefront != NULL && + mwavefront->lo <= k+1 && + k+1 <= mwavefront->hi) { + return BACKTRACE_PIGGYBACK_SET(mwavefront->offsets[k+1],backtrace_D2_open); + } else { + return WAVEFRONT_OFFSET_NULL; + } +} +int64_t wavefront_backtrace_del1_ext( + wavefront_aligner_t* const wf_aligner, + const int score, + const int k) { + if (score < 0) return WAVEFRONT_OFFSET_NULL; + wavefront_t* const d1wavefront = wf_aligner->wf_components.d1wavefronts[score]; + if (d1wavefront != NULL && + d1wavefront->lo <= k+1 && + k+1 <= d1wavefront->hi) { + return BACKTRACE_PIGGYBACK_SET(d1wavefront->offsets[k+1],backtrace_D1_ext); + } else { + return WAVEFRONT_OFFSET_NULL; + } +} +int64_t wavefront_backtrace_del2_ext( + wavefront_aligner_t* const wf_aligner, + const int score, + const int k) { + if (score < 0) return WAVEFRONT_OFFSET_NULL; + wavefront_t* const d2wavefront = wf_aligner->wf_components.d2wavefronts[score]; + if (d2wavefront != NULL && + d2wavefront->lo <= k+1 && + k+1 <= d2wavefront->hi) { + return BACKTRACE_PIGGYBACK_SET(d2wavefront->offsets[k+1],backtrace_D2_ext); + } else { + return WAVEFRONT_OFFSET_NULL; + } +} +/* + * Backtrace Trace Patch Insertion + */ +int64_t wavefront_backtrace_ins1_open( + wavefront_aligner_t* const wf_aligner, + const int score, + const int k) { + if (score < 0) return WAVEFRONT_OFFSET_NULL; + wavefront_t* const mwavefront = wf_aligner->wf_components.mwavefronts[score]; + if (mwavefront != NULL && + mwavefront->lo <= k-1 && + k-1 <= mwavefront->hi) { + return BACKTRACE_PIGGYBACK_SET(mwavefront->offsets[k-1]+1,backtrace_I1_open); + } else { + return WAVEFRONT_OFFSET_NULL; + } +} +int64_t wavefront_backtrace_ins2_open( + wavefront_aligner_t* const wf_aligner, + const int score, + const int k) { + if (score < 0) return WAVEFRONT_OFFSET_NULL; + wavefront_t* const mwavefront = wf_aligner->wf_components.mwavefronts[score]; + if (mwavefront != NULL && + mwavefront->lo <= k-1 && + k-1 <= mwavefront->hi) { + return BACKTRACE_PIGGYBACK_SET(mwavefront->offsets[k-1]+1,backtrace_I2_open); + } else { + return WAVEFRONT_OFFSET_NULL; + } +} +int64_t wavefront_backtrace_ins1_ext( + wavefront_aligner_t* const wf_aligner, + const int score, + const int k) { + if (score < 0) return WAVEFRONT_OFFSET_NULL; + wavefront_t* const i1wavefront = wf_aligner->wf_components.i1wavefronts[score]; + if (i1wavefront != NULL && + i1wavefront->lo <= k-1 && + k-1 <= i1wavefront->hi) { + return BACKTRACE_PIGGYBACK_SET(i1wavefront->offsets[k-1]+1,backtrace_I1_ext); + } else { + return WAVEFRONT_OFFSET_NULL; + } +} +int64_t wavefront_backtrace_ins2_ext( + wavefront_aligner_t* const wf_aligner, + const int score, + const int k) { + if (score < 0) return WAVEFRONT_OFFSET_NULL; + wavefront_t* const i2wavefront = wf_aligner->wf_components.i2wavefronts[score]; + if (i2wavefront != NULL && + i2wavefront->lo <= k-1 && + k-1 <= i2wavefront->hi) { + return BACKTRACE_PIGGYBACK_SET(i2wavefront->offsets[k-1]+1,backtrace_I2_ext); + } else { + return WAVEFRONT_OFFSET_NULL; + } +} +/* + * Backtrace wavefronts + */ +void wavefront_backtrace_linear( + wavefront_aligner_t* const wf_aligner, + const int alignment_score, + const int alignment_k, + const wf_offset_t alignment_offset) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + const wavefront_penalties_t* const penalties = &wf_aligner->penalties; + const distance_metric_t distance_metric = penalties->distance_metric; + // Prepare cigar + cigar_t* const cigar = wf_aligner->cigar; + cigar->end_offset = cigar->max_operations - 1; + cigar->begin_offset = cigar->max_operations - 2; + cigar->operations[cigar->end_offset] = '\0'; + // Compute starting location + int score = alignment_score; + int k = alignment_k; + int h = WAVEFRONT_H(alignment_k,alignment_offset); + int v = WAVEFRONT_V(alignment_k,alignment_offset); + wf_offset_t offset = alignment_offset; + // Account for ending insertions/deletions + if (v < pattern_length) { + int i = pattern_length - v; + while (i > 0) {cigar->operations[(cigar->begin_offset)--] = 'D'; --i;}; + } + if (h < text_length) { + int i = text_length - h; + while (i > 0) {cigar->operations[(cigar->begin_offset)--] = 'I'; --i;}; + } + // Trace the alignment back + while (v > 0 && h > 0 && score > 0) { + // Compute scores + const int mismatch = score - penalties->mismatch; + const int gap_open1 = score - penalties->gap_opening1; + // Compute source offsets + const int64_t misms = (distance_metric != indel) ? + wavefront_backtrace_misms(wf_aligner,mismatch,k) : + WAVEFRONT_OFFSET_NULL; + const int64_t ins = wavefront_backtrace_ins1_open(wf_aligner,gap_open1,k); + const int64_t del = wavefront_backtrace_del1_open(wf_aligner,gap_open1,k); + const int64_t max_all = MAX(misms,MAX(ins,del)); + // Check source score + if (max_all < 0) break; // No source + // Traceback Matches + const int max_offset = BACKTRACE_PIGGYBACK_GET_OFFSET(max_all); + const int num_matches = offset - max_offset; + wavefront_backtrace_matches(wf_aligner,k,offset,num_matches,cigar); + offset = max_offset; + // Update coordinates + v = WAVEFRONT_V(k,offset); + h = WAVEFRONT_H(k,offset); + if (v <= 0 || h <= 0) break; + // Traceback Operation + const backtrace_type backtrace_type = BACKTRACE_PIGGYBACK_GET_TYPE(max_all); + switch (backtrace_type) { + case backtrace_M: + score = mismatch; + cigar->operations[(cigar->begin_offset)--] = 'X'; + --offset; + break; + case backtrace_I1_open: + score = gap_open1; + cigar->operations[(cigar->begin_offset)--] = 'I'; + --k; --offset; + break; + case backtrace_D1_open: + score = gap_open1; + cigar->operations[(cigar->begin_offset)--] = 'D'; + ++k; + break; + default: + fprintf(stderr,"[WFA::Backtrace] Wrong type trace.4\n"); + exit(1); + break; + } + // Update coordinates + v = WAVEFRONT_V(k,offset); + h = WAVEFRONT_H(k,offset); + } + // Account for last operations + if (v > 0 && h > 0) { + // Account for beginning series of matches + const int num_matches = MIN(v,h); + wavefront_backtrace_matches(wf_aligner,k,offset,num_matches,cigar); + v -= num_matches; + h -= num_matches; + } + // Account for beginning insertions/deletions + while (v > 0) {cigar->operations[(cigar->begin_offset)--] = 'D'; --v;}; + while (h > 0) {cigar->operations[(cigar->begin_offset)--] = 'I'; --h;}; + // Set CIGAR + ++(cigar->begin_offset); + cigar->score = alignment_score; +} +void wavefront_backtrace_affine( + wavefront_aligner_t* const wf_aligner, + const affine2p_matrix_type component_begin, + const affine2p_matrix_type component_end, + const int alignment_score, + const int alignment_k, + const wf_offset_t alignment_offset) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + const wavefront_penalties_t* const penalties = &wf_aligner->penalties; + const distance_metric_t distance_metric = penalties->distance_metric; + // Prepare cigar + cigar_t* const cigar = wf_aligner->cigar; + cigar->end_offset = cigar->max_operations - 1; + cigar->begin_offset = cigar->max_operations - 2; + cigar->operations[cigar->end_offset] = '\0'; + // Compute starting location + affine2p_matrix_type matrix_type = component_end; + int score = alignment_score; + int k = alignment_k; + int h = WAVEFRONT_H(alignment_k,alignment_offset); + int v = WAVEFRONT_V(alignment_k,alignment_offset); + wf_offset_t offset = alignment_offset; + // Account for ending insertions/deletions + if (component_end == affine2p_matrix_M) { // ends-free + if (v < pattern_length) { + int i = pattern_length - v; + while (i > 0) {cigar->operations[(cigar->begin_offset)--] = 'D'; --i;}; + } + if (h < text_length) { + int i = text_length - h; + while (i > 0) {cigar->operations[(cigar->begin_offset)--] = 'I'; --i;}; + } + } + // Trace the alignment back + while (v > 0 && h > 0 && score > 0) { + // Compute scores + const int mismatch = score - penalties->mismatch; + const int gap_open1 = score - penalties->gap_opening1 - penalties->gap_extension1; + const int gap_open2 = score - penalties->gap_opening2 - penalties->gap_extension2; + const int gap_extend1 = score - penalties->gap_extension1; + const int gap_extend2 = score - penalties->gap_extension2; + // Compute source offsets + int64_t max_all; + switch (matrix_type) { + case affine2p_matrix_M: { + const int64_t misms = wavefront_backtrace_misms(wf_aligner,mismatch,k); + const int64_t ins1_open = wavefront_backtrace_ins1_open(wf_aligner,gap_open1,k); + const int64_t ins1_ext = wavefront_backtrace_ins1_ext(wf_aligner,gap_extend1,k); + const int64_t max_ins1 = MAX(ins1_open,ins1_ext); + const int64_t del1_open = wavefront_backtrace_del1_open(wf_aligner,gap_open1,k); + const int64_t del1_ext = wavefront_backtrace_del1_ext(wf_aligner,gap_extend1,k); + const int64_t max_del1 = MAX(del1_open,del1_ext); + if (distance_metric == gap_affine) { + max_all = MAX(misms,MAX(max_ins1,max_del1)); + break; + } + const int64_t ins2_open = wavefront_backtrace_ins2_open(wf_aligner,gap_open2,k); + const int64_t ins2_ext = wavefront_backtrace_ins2_ext(wf_aligner,gap_extend2,k); + const int64_t max_ins2 = MAX(ins2_open,ins2_ext); + const int64_t del2_open = wavefront_backtrace_del2_open(wf_aligner,gap_open2,k); + const int64_t del2_ext = wavefront_backtrace_del2_ext(wf_aligner,gap_extend2,k); + const int64_t max_del2 = MAX(del2_open,del2_ext); + const int64_t max_ins = MAX(max_ins1,max_ins2); + const int64_t max_del = MAX(max_del1,max_del2); + max_all = MAX(misms,MAX(max_ins,max_del)); + break; + } + case affine2p_matrix_I1: { + const int64_t ins1_open = wavefront_backtrace_ins1_open(wf_aligner,gap_open1,k); + const int64_t ins1_ext = wavefront_backtrace_ins1_ext(wf_aligner,gap_extend1,k); + max_all = MAX(ins1_open,ins1_ext); + break; + } + case affine2p_matrix_I2: { + const int64_t ins2_open = wavefront_backtrace_ins2_open(wf_aligner,gap_open2,k); + const int64_t ins2_ext = wavefront_backtrace_ins2_ext(wf_aligner,gap_extend2,k); + max_all = MAX(ins2_open,ins2_ext); + break; + } + case affine2p_matrix_D1: { + const int64_t del1_open = wavefront_backtrace_del1_open(wf_aligner,gap_open1,k); + const int64_t del1_ext = wavefront_backtrace_del1_ext(wf_aligner,gap_extend1,k); + max_all = MAX(del1_open,del1_ext); + break; + } + case affine2p_matrix_D2: { + const int64_t del2_open = wavefront_backtrace_del2_open(wf_aligner,gap_open2,k); + const int64_t del2_ext = wavefront_backtrace_del2_ext(wf_aligner,gap_extend2,k); + max_all = MAX(del2_open,del2_ext); + break; + } + default: + fprintf(stderr,"[WFA::Backtrace] Wrong type trace.1\n"); + exit(1); + break; + } + // Check source score + if (max_all < 0) break; // No source + // Traceback Matches + if (matrix_type == affine2p_matrix_M) { + const int max_offset = BACKTRACE_PIGGYBACK_GET_OFFSET(max_all); + const int num_matches = offset - max_offset; + wavefront_backtrace_matches(wf_aligner,k,offset,num_matches,cigar); + offset = max_offset; + // Update coordinates + v = WAVEFRONT_V(k,offset); + h = WAVEFRONT_H(k,offset); + if (v <= 0 || h <= 0) break; + } + // Traceback Operation + const backtrace_type backtrace_type = BACKTRACE_PIGGYBACK_GET_TYPE(max_all); + switch (backtrace_type) { + case backtrace_M: + score = mismatch; + matrix_type = affine2p_matrix_M; + break; + case backtrace_I1_open: + score = gap_open1; + matrix_type = affine2p_matrix_M; + break; + case backtrace_I1_ext: + score = gap_extend1; + matrix_type = affine2p_matrix_I1; + break; + case backtrace_I2_open: + score = gap_open2; + matrix_type = affine2p_matrix_M; + break; + case backtrace_I2_ext: + score = gap_extend2; + matrix_type = affine2p_matrix_I2; + break; + case backtrace_D1_open: + score = gap_open1; + matrix_type = affine2p_matrix_M; + break; + case backtrace_D1_ext: + score = gap_extend1; + matrix_type = affine2p_matrix_D1; + break; + case backtrace_D2_open: + score = gap_open2; + matrix_type = affine2p_matrix_M; + break; + case backtrace_D2_ext: + score = gap_extend2; + matrix_type = affine2p_matrix_D2; + break; + default: + fprintf(stderr,"[WFA::Backtrace] Wrong type trace.2\n"); + exit(1); + break; + } + switch (backtrace_type) { + case backtrace_M: + cigar->operations[(cigar->begin_offset)--] = 'X'; + --offset; + break; + case backtrace_I1_open: + case backtrace_I1_ext: + case backtrace_I2_open: + case backtrace_I2_ext: + cigar->operations[(cigar->begin_offset)--] = 'I'; + --k; --offset; + break; + case backtrace_D1_open: + case backtrace_D1_ext: + case backtrace_D2_open: + case backtrace_D2_ext: + cigar->operations[(cigar->begin_offset)--] = 'D'; + ++k; + break; + default: + fprintf(stderr,"[WFA::Backtrace] Wrong type trace.3\n"); + exit(1); + break; + } + // Update coordinates + v = WAVEFRONT_V(k,offset); + h = WAVEFRONT_H(k,offset); + } + // Account for last operations + if (matrix_type == affine2p_matrix_M) { + if (v > 0 && h > 0) { + // Account for beginning series of matches + const int num_matches = MIN(v,h); + wavefront_backtrace_matches(wf_aligner,k,offset,num_matches,cigar); + v -= num_matches; + h -= num_matches; + } + // Account for beginning insertions/deletions + while (v > 0) {cigar->operations[(cigar->begin_offset)--] = 'D'; --v;}; + while (h > 0) {cigar->operations[(cigar->begin_offset)--] = 'I'; --h;}; + } else { + // DEBUG + if (v != 0 || h != 0 || (score != 0 && penalties->match == 0)) { + fprintf(stderr,"[WFA::Backtrace] I?/D?-Beginning backtrace error\n"); + fprintf(stderr,">%.*s\n",pattern_length,wf_aligner->pattern); + fprintf(stderr,"<%.*s\n",text_length,wf_aligner->text); + exit(-1); + } + } + // Set CIGAR + ++(cigar->begin_offset); + cigar->score = alignment_score; +} +/* + * Backtrace from BT-Buffer (pcigar) + */ +void wavefront_backtrace_pcigar( + wavefront_aligner_t* const wf_aligner, + const int alignment_k, + const int alignment_offset, + const pcigar_t pcigar_last, + const bt_block_idx_t prev_idx_last) { + // Parameters + wf_backtrace_buffer_t* const bt_buffer = wf_aligner->wf_components.bt_buffer; + // Traceback pcigar-blocks + bt_block_t bt_block_last = { + .pcigar = pcigar_last, + .prev_idx = prev_idx_last + }; + bt_block_t* const init_block = wf_backtrace_buffer_traceback_pcigar(bt_buffer,&bt_block_last); + // Fetch initial coordinate + const int init_position_offset = init_block->pcigar; + wf_backtrace_init_pos_t* const backtrace_init_pos = + vector_get_elm(bt_buffer->alignment_init_pos,init_position_offset,wf_backtrace_init_pos_t); + // Unpack pcigar blocks (packed alignment) + const int begin_v = backtrace_init_pos->v; + const int begin_h = backtrace_init_pos->h; + const int end_v = WAVEFRONT_V(alignment_k,alignment_offset); + const int end_h = WAVEFRONT_H(alignment_k,alignment_offset); + if (wf_aligner->penalties.distance_metric <= gap_linear) { + wf_backtrace_buffer_unpack_cigar_linear(bt_buffer, + wf_aligner->pattern,wf_aligner->pattern_length, + wf_aligner->text,wf_aligner->text_length, + wf_aligner->match_funct, + wf_aligner->match_funct_arguments, + begin_v,begin_h,end_v,end_h,wf_aligner->cigar); + } else { + wf_backtrace_buffer_unpack_cigar_affine(bt_buffer, + wf_aligner->pattern,wf_aligner->pattern_length, + wf_aligner->text,wf_aligner->text_length, + wf_aligner->match_funct, + wf_aligner->match_funct_arguments, + begin_v,begin_h,end_v,end_h,wf_aligner->cigar); + } +} diff --git a/src/lib/wfa2/wavefront/wavefront_backtrace.h b/src/lib/wfa2/wavefront/wavefront_backtrace.h new file mode 100644 index 000000000..139eced50 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_backtrace.h @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront-Alignment module for backtracing alignments + */ + +#ifndef WAVEFRONT_BACKTRACE_H_ +#define WAVEFRONT_BACKTRACE_H_ + +#include "wavefront_aligner.h" + +/* + * Backtrace wavefronts + */ +void wavefront_backtrace_linear( + wavefront_aligner_t* const wf_aligner, + const int alignment_score, + const int alignment_k, + const wf_offset_t alignment_offset); +void wavefront_backtrace_affine( + wavefront_aligner_t* const wf_aligner, + const affine2p_matrix_type component_begin, + const affine2p_matrix_type component_end, + const int alignment_score, + const int alignment_k, + const wf_offset_t alignment_offset); + +/* + * Backtrace from BT-Buffer (pcigar) + */ +void wavefront_backtrace_pcigar( + wavefront_aligner_t* const wf_aligner, + const int alignment_k, + const int alignment_offset, + const pcigar_t pcigar_last, + const bt_block_idx_t prev_idx_last); + +#endif /* WAVEFRONT_BACKTRACE_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_backtrace_buffer.c b/src/lib/wfa2/wavefront/wavefront_backtrace_buffer.c new file mode 100644 index 000000000..098f20b0c --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_backtrace_buffer.c @@ -0,0 +1,531 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront backtrace buffer to store bactrace-blocks + */ + +#include "wavefront_backtrace_buffer.h" + +/* + * Config + */ +#define BT_BUFFER_SEGMENT_LENGTH BUFFER_SIZE_8M + +#define BT_BUFFER_SEGMENT_IDX(block_idx) ((block_idx)/BT_BUFFER_SEGMENT_LENGTH) +#define BT_BUFFER_SEGMENT_OFFSET(block_idx) ((block_idx)%BT_BUFFER_SEGMENT_LENGTH) + +#define BT_BUFFER_IDX(segment_idx,segment_offset) \ + ((segment_idx)*BT_BUFFER_SEGMENT_LENGTH) + (segment_offset) + +/* + * BT-Block Segments + */ +void wf_backtrace_buffer_segment_add( + wf_backtrace_buffer_t* const bt_buffer) { + bt_block_t* const bt_segment = mm_allocator_calloc( + bt_buffer->mm_allocator,BT_BUFFER_SEGMENT_LENGTH,bt_block_t,false); + vector_insert(bt_buffer->segments,bt_segment,bt_block_t*); +} +void wf_backtrace_buffer_segment_reserve( + wf_backtrace_buffer_t* const bt_buffer) { + // Reset position + bt_buffer->segment_offset = 0; + ++(bt_buffer->segment_idx); + // Check segments + if (bt_buffer->segment_idx >= vector_get_used(bt_buffer->segments)) { + // Check segment position + const uint64_t block_idx = ((uint64_t)bt_buffer->segment_idx+1) * BT_BUFFER_SEGMENT_LENGTH; + if (block_idx >= BT_BLOCK_IDX_MAX) { + fprintf(stderr,"[WFA::BacktraceBuffer] Reached maximum addressable index"); exit(-1); + } + // Add segment + wf_backtrace_buffer_segment_add(bt_buffer); + } + // Set pointer to next block free + bt_block_t** const segments = vector_get_mem(bt_buffer->segments,bt_block_t*); + bt_buffer->block_next = segments[bt_buffer->segment_idx]; +} +/* + * Setup + */ +wf_backtrace_buffer_t* wf_backtrace_buffer_new( + mm_allocator_t* const mm_allocator) { + // Alloc + wf_backtrace_buffer_t* const bt_buffer = + mm_allocator_alloc(mm_allocator,wf_backtrace_buffer_t); + bt_buffer->mm_allocator = mm_allocator; + // Initialize + bt_buffer->segment_idx = 0; + bt_buffer->segment_offset = 0; + bt_buffer->segments = vector_new(10,bt_block_t*); + wf_backtrace_buffer_segment_add(bt_buffer); // Add initial segment + bt_buffer->block_next = vector_get_mem(bt_buffer->segments,bt_block_t*)[0]; + bt_buffer->num_compacted_blocks = 0; + bt_buffer->num_compactions = 0; + bt_buffer->alignment_init_pos = vector_new(100,wf_backtrace_init_pos_t); + bt_buffer->alignment_packed = vector_new(100,pcigar_t); + bt_buffer->prefetch_blocks_idxs = vector_new(500,bt_block_idx_t); + // Return + return bt_buffer; +} +void wf_backtrace_buffer_clear( + wf_backtrace_buffer_t* const bt_buffer) { + bt_buffer->segment_idx = 0; + bt_buffer->segment_offset = 0; + bt_buffer->block_next = vector_get_mem(bt_buffer->segments,bt_block_t*)[0]; + bt_buffer->num_compacted_blocks = 0; + bt_buffer->num_compactions = 0; + vector_clear(bt_buffer->alignment_init_pos); +} +void wf_backtrace_buffer_reap( + wf_backtrace_buffer_t* const bt_buffer) { + // Reap segments beyond the first + const int num_segments = vector_get_used(bt_buffer->segments); + bt_block_t** const segments = vector_get_mem(bt_buffer->segments,bt_block_t*); + int i; + for (i=1;imm_allocator,segments[i]); + } + vector_set_used(bt_buffer->segments,1); + // Clear + bt_buffer->segment_idx = 0; + bt_buffer->segment_offset = 0; + bt_buffer->block_next = vector_get_mem(bt_buffer->segments,bt_block_t*)[0]; + bt_buffer->num_compacted_blocks = 0; + bt_buffer->num_compactions = 0; +} +void wf_backtrace_buffer_delete( + wf_backtrace_buffer_t* const bt_buffer) { + // Free segments + const int num_segments = vector_get_used(bt_buffer->segments); + bt_block_t** const segments = vector_get_mem(bt_buffer->segments,bt_block_t*); + int i; + for (i=0;imm_allocator,segments[i]); + } + // Free handlers + vector_delete(bt_buffer->segments); + vector_delete(bt_buffer->alignment_init_pos); + vector_delete(bt_buffer->alignment_packed); + vector_delete(bt_buffer->prefetch_blocks_idxs); + mm_allocator_free(bt_buffer->mm_allocator,bt_buffer); +} +/* + * Accessors + */ +uint64_t wf_backtrace_buffer_get_used( + wf_backtrace_buffer_t* const bt_buffer) { + const bt_block_idx_t max_block_idx = BT_BUFFER_IDX(bt_buffer->segment_idx,bt_buffer->segment_offset); + return max_block_idx; +} +bt_block_idx_t wf_backtrace_buffer_get_num_compacted_blocks( + wf_backtrace_buffer_t* const bt_buffer) { + return bt_buffer->num_compacted_blocks; +} +void wf_backtrace_buffer_set_num_compacted_blocks( + wf_backtrace_buffer_t* const bt_buffer, + const bt_block_idx_t num_compacted_blocks) { + bt_buffer->num_compacted_blocks = num_compacted_blocks; +} +void wf_backtrace_buffer_reset_compaction( + wf_backtrace_buffer_t* const bt_buffer) { + bt_buffer->num_compactions = 0; + bt_buffer->num_compacted_blocks = 0; +} +uint64_t wf_backtrace_buffer_get_size_allocated( + wf_backtrace_buffer_t* const bt_buffer) { + const uint64_t segments_used = vector_get_used(bt_buffer->segments); + return segments_used*BT_BUFFER_SEGMENT_LENGTH*sizeof(bt_block_t); +} +uint64_t wf_backtrace_buffer_get_size_used( + wf_backtrace_buffer_t* const bt_buffer) { + const bt_block_idx_t max_block_idx = BT_BUFFER_IDX(bt_buffer->segment_idx,bt_buffer->segment_offset); + return max_block_idx*sizeof(bt_block_t); +} +void wf_backtrace_buffer_prefetch_block( + wf_backtrace_buffer_t* const bt_buffer, + const bt_block_idx_t block_idx) { + // Compute location + const int segment_idx = BT_BUFFER_SEGMENT_IDX(block_idx); + const int segment_offset = BT_BUFFER_SEGMENT_OFFSET(block_idx); + // Fetch bt-block + bt_block_t** const segments = vector_get_mem(bt_buffer->segments,bt_block_t*); + PREFETCH(segments[segment_idx]+segment_offset); +} +bt_block_t* wf_backtrace_buffer_get_block( + wf_backtrace_buffer_t* const bt_buffer, + const bt_block_idx_t block_idx) { + // Compute location + const int segment_idx = BT_BUFFER_SEGMENT_IDX(block_idx); + const int segment_offset = BT_BUFFER_SEGMENT_OFFSET(block_idx); + // Fetch bt-block + bt_block_t** const segments = vector_get_mem(bt_buffer->segments,bt_block_t*); + return &(segments[segment_idx][segment_offset]); +} +void wf_backtrace_buffer_add_used( + wf_backtrace_buffer_t* const bt_buffer, + const int used) { + // Next + bt_buffer->segment_offset += used; + bt_buffer->block_next += used; + // Reserve + if (bt_buffer->segment_offset >= BT_BUFFER_SEGMENT_LENGTH) { + wf_backtrace_buffer_segment_reserve(bt_buffer); + } +} +bt_block_idx_t wf_backtrace_buffer_get_mem( + wf_backtrace_buffer_t* const bt_buffer, + bt_block_t** const bt_block_mem, + int* const bt_blocks_available) { + // Parameters + const int segment_idx = bt_buffer->segment_idx; + const int segment_offset = bt_buffer->segment_offset; + // Get total available blocks + *bt_block_mem = bt_buffer->block_next; + *bt_blocks_available = BT_BUFFER_SEGMENT_LENGTH - bt_buffer->segment_offset; + // Return current global position + return BT_BUFFER_IDX(segment_idx,segment_offset); +} +/* + * Store blocks + */ +void wf_backtrace_buffer_store_block( + wf_backtrace_buffer_t* const bt_buffer, + const pcigar_t pcigar, + const bt_block_idx_t prev_idx) { + // Store BT-block + bt_buffer->block_next->pcigar = pcigar; + bt_buffer->block_next->prev_idx = prev_idx; + // Next + ++(bt_buffer->block_next); + ++(bt_buffer->segment_offset); + // Reserve + if (bt_buffer->segment_offset >= BT_BUFFER_SEGMENT_LENGTH) { + wf_backtrace_buffer_segment_reserve(bt_buffer); + } +} +bt_block_idx_t wf_backtrace_buffer_init_block( + wf_backtrace_buffer_t* const bt_buffer, + const int v, + const int h) { + // Parameters + const int segment_idx = bt_buffer->segment_idx; + const int segment_offset = bt_buffer->segment_offset; + // Store initial position (v,h) + const int init_position_offset = vector_get_used(bt_buffer->alignment_init_pos); + wf_backtrace_init_pos_t init_pos = { .v = v, .h = h }; + vector_insert(bt_buffer->alignment_init_pos,init_pos,wf_backtrace_init_pos_t); + // Store BT-block (Index to initial position,NULL prev) + wf_backtrace_buffer_store_block(bt_buffer,init_position_offset,BT_BLOCK_IDX_NULL); + // Return current index + return BT_BUFFER_IDX(segment_idx,segment_offset); +} +/* + * Unpack CIGAR + */ +bt_block_t* wf_backtrace_buffer_traceback_pcigar( + wf_backtrace_buffer_t* const bt_buffer, + bt_block_t* bt_block) { + // Clear temporal buffer + vector_t* const alignment_packed = bt_buffer->alignment_packed; + vector_clear(alignment_packed); + // Traverse-back the BT-blocks and store all the pcigars + while (bt_block->prev_idx != BT_BLOCK_IDX_NULL) { + vector_insert(alignment_packed,bt_block->pcigar,pcigar_t); + const bt_block_idx_t prev_idx = bt_block->prev_idx; + bt_block = wf_backtrace_buffer_get_block(bt_buffer,prev_idx); + } + // Return initial block (start coordinate) + return bt_block; +} +void wf_backtrace_buffer_unpack_cigar_linear( + wf_backtrace_buffer_t* const bt_buffer, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + alignment_match_funct_t const match_funct, + void* const match_funct_arguments, + const int begin_v, + const int begin_h, + const int end_v, + const int end_h, + cigar_t* const cigar) { + // Clear cigar + char* cigar_buffer = cigar->operations; + cigar->begin_offset = 0; + // Add init insertions/deletions + int i; + int v = begin_v; + int h = begin_h; + for (i=0;ialignment_packed); + pcigar_t* const palignment_blocks = vector_get_mem(bt_buffer->alignment_packed,pcigar_t); + for (i=num_palignment_blocks-1;i>=0;--i) { + // Unpack block + int cigar_block_length = 0; + pcigar_unpack_linear( + palignment_blocks[i], + pattern,pattern_length,text,text_length, + match_funct,match_funct_arguments,&v,&h, + cigar_buffer,&cigar_block_length); + // Update CIGAR + cigar_buffer += cigar_block_length; + } + // Account for last stroke of matches + const int num_matches = MIN(end_v-v,end_h-h); + for (i=0;iend_offset = cigar_buffer - cigar->operations; +} +void wf_backtrace_buffer_unpack_cigar_affine( + wf_backtrace_buffer_t* const bt_buffer, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + alignment_match_funct_t const match_funct, + void* const match_funct_arguments, + const int begin_v, + const int begin_h, + const int end_v, + const int end_h, + cigar_t* const cigar) { + // Clear cigar + char* cigar_buffer = cigar->operations; + cigar->begin_offset = 0; + // Add init insertions/deletions + int i; + int v = begin_v; + int h = begin_h; + for (i=0;ialignment_packed); + pcigar_t* const palignment_blocks = vector_get_mem(bt_buffer->alignment_packed,pcigar_t); + affine_matrix_type current_matrix_type = affine_matrix_M; + for (i=num_palignment_blocks-1;i>=0;--i) { + // Unpack block + int cigar_block_length = 0; + pcigar_unpack_affine( + palignment_blocks[i], + pattern,pattern_length,text,text_length, + match_funct,match_funct_arguments,&v,&h, + cigar_buffer,&cigar_block_length,¤t_matrix_type); + // Update CIGAR + cigar_buffer += cigar_block_length; + } + // Account for last stroke of matches + const int num_matches = MIN(end_v-v,end_h-h); + for (i=0;iend_offset = cigar_buffer - cigar->operations; +} +/* + * Compact + */ +void wf_backtrace_buffer_mark_backtrace( + wf_backtrace_buffer_t* const bt_buffer, + const bt_block_idx_t bt_block_idx, + bitmap_t* const bitmap) { + // Parameters + const bt_block_idx_t num_compacted_blocks = bt_buffer->num_compacted_blocks; + // Traverse-back the BT-blocks while not marked + bt_block_t bt_block_last = { .prev_idx = bt_block_idx }; + bt_block_t* bt_block = &bt_block_last; + // Check marked and fetch previous (until already marked or NULL is found) + while (bt_block->prev_idx != BT_BLOCK_IDX_NULL && + bt_block->prev_idx >= num_compacted_blocks && + !bitmap_check__set(bitmap,bt_block->prev_idx)) { + // Fetch previous BT-block + const bt_block_idx_t prev_idx = bt_block->prev_idx; + bt_block = wf_backtrace_buffer_get_block(bt_buffer,prev_idx); + } +} +void wf_backtrace_buffer_mark_backtrace_batch( + wf_backtrace_buffer_t* const bt_buffer, + wf_offset_t* const offsets, + bt_block_idx_t* const bt_block_idxs, + const int num_block_idxs, + bitmap_t* const bitmap) { + // Parameters + const bt_block_idx_t num_compacted_blocks = bt_buffer->num_compacted_blocks; + // Reserve prefetch-buffer + const int max_batch_size = 100; + vector_reserve(bt_buffer->prefetch_blocks_idxs,max_batch_size,false); + bt_block_idx_t* const pf_block_idx = vector_get_mem(bt_buffer->prefetch_blocks_idxs,bt_block_idx_t); + // Fill-in loop (+ initial prefetch) + int active_blocks = 0, next_idx = 0; + while (active_blocks < max_batch_size && next_idx < num_block_idxs) { + // Check NULL + const bt_block_idx_t block_idx = bt_block_idxs[next_idx]; + if (offsets[next_idx] >= 0 && + block_idx >= num_compacted_blocks) { // NOTE block_idx != BT_BLOCK_IDX_NULL + // Prefetch (bt-block and bt_block) + BITMAP_PREFETCH_BLOCK(bitmap,block_idx); + wf_backtrace_buffer_prefetch_block(bt_buffer,block_idx); + // Store + pf_block_idx[active_blocks] = block_idx; + ++active_blocks; + } + ++next_idx; // Next + } + // Batch process+prefetch loop + int i = 0; + while (active_blocks > 0) { + // Fetch BT-block & BM-block + const bt_block_idx_t block_idx = pf_block_idx[i]; + BITMAP_GET_BLOCK(bitmap,block_idx,block_bm_ptr); + // Check marked + if (!BM_BLOCK_IS_SET(*block_bm_ptr,block_idx)) { + BM_BLOCK_SET(*block_bm_ptr,block_idx); + // Fetch next block + bt_block_t* const bt_block = wf_backtrace_buffer_get_block(bt_buffer,block_idx); + const bt_block_idx_t prev_block_idx = bt_block->prev_idx; + // Check NULL + if (prev_block_idx != BT_BLOCK_IDX_NULL && + prev_block_idx >= num_compacted_blocks) { + // Update next bt-block index and prefetch + pf_block_idx[i] = prev_block_idx; + BITMAP_PREFETCH_BLOCK(bitmap,prev_block_idx); + wf_backtrace_buffer_prefetch_block(bt_buffer,prev_block_idx); + // Next in batch + i = (i+1) % active_blocks; + continue; + } + } + // Refill + while (true /* !refilled */) { + if (next_idx < num_block_idxs) { + // Check NULL + if (offsets[next_idx] < 0 || bt_block_idxs[next_idx] < num_compacted_blocks) { + ++next_idx; // Next + continue; + } + // Prefetch (bt-block and bt_block) + const bt_block_idx_t block_idx = bt_block_idxs[next_idx]; + BITMAP_PREFETCH_BLOCK(bitmap,block_idx); + wf_backtrace_buffer_prefetch_block(bt_buffer,block_idx); + // Store + pf_block_idx[i] = block_idx; + ++next_idx; + // Next in batch + i = (i+1) % active_blocks; + break; + } else { + // Take the last in the batch + --active_blocks; + pf_block_idx[i] = pf_block_idx[active_blocks]; + // Next in batch + if (active_blocks) i = (i+1) % active_blocks; + break; + } + } + } +} +bt_block_idx_t wf_backtrace_buffer_compact_marked( + wf_backtrace_buffer_t* const bt_buffer, + bitmap_t* const bitmap, + const int verbose) { + // Parameters + const int num_segments = vector_get_used(bt_buffer->segments); + bt_block_t** const segments = vector_get_mem(bt_buffer->segments,bt_block_t*); + const bt_block_idx_t num_compacted_blocks = bt_buffer->num_compacted_blocks; + // Sentinels + bt_block_idx_t read_global_pos = num_compacted_blocks; + bt_block_idx_t write_global_pos = num_compacted_blocks; + bt_block_idx_t read_segidx = BT_BUFFER_SEGMENT_IDX(read_global_pos); + bt_block_idx_t read_offset = BT_BUFFER_SEGMENT_OFFSET(read_global_pos); + bt_block_idx_t write_segidx = BT_BUFFER_SEGMENT_IDX(write_global_pos); + bt_block_idx_t write_offset = BT_BUFFER_SEGMENT_OFFSET(write_global_pos); + bt_block_t* read_block = segments[read_segidx] + read_offset; + bt_block_t* write_block = segments[write_segidx] + write_offset; + // Traverse all BT-blocks from the beginning (stored marked) + const bt_block_idx_t max_block_idx = BT_BUFFER_IDX(bt_buffer->segment_idx,bt_buffer->segment_offset); + while (read_global_pos < max_block_idx) { + // Check marked block + BITMAP_GET_BLOCK(bitmap,read_global_pos,block_bitmap_ptr); + if (BM_BLOCK_IS_SET(*block_bitmap_ptr,read_global_pos)) { + // Store pcigar in compacted BT-buffer + write_block->pcigar = read_block->pcigar; + // Translate and store index in compacted BT-buffer + if (read_block->prev_idx == BT_BLOCK_IDX_NULL || + read_block->prev_idx < num_compacted_blocks) { + write_block->prev_idx = read_block->prev_idx; + } else { + write_block->prev_idx = num_compacted_blocks + bitmap_erank(bitmap,read_block->prev_idx); + } + // Next write + ++write_offset; ++write_block; ++write_global_pos; + if (write_offset >= BT_BUFFER_SEGMENT_LENGTH) { + // Next segment + write_block = segments[++write_segidx]; + write_offset = 0; + } + } + // Next read + ++read_offset; ++read_block; ++read_global_pos; + if (read_offset >= BT_BUFFER_SEGMENT_LENGTH) { + // Next segment + if (++read_segidx >= num_segments) break; + read_block = segments[read_segidx]; + read_offset = 0; + } + } + // Update next BT-buffer index + bt_buffer->segment_offset = write_offset; + bt_buffer->segment_idx = write_segidx; + bt_buffer->block_next = write_block; + bt_buffer->num_compactions++; + // DEBUG + if (verbose >= 3) { + fprintf(stderr,"[WFA::BacktraceBuffer] Compacted from %lu MB to %lu MB (%2.2f%%)", + CONVERT_B_TO_MB(read_global_pos*sizeof(bt_block_t)), + CONVERT_B_TO_MB(write_global_pos*sizeof(bt_block_t)), + 100.0f*(float)write_global_pos/(float)read_global_pos); + } + // Return last index + return write_global_pos - 1; +} + + + diff --git a/src/lib/wfa2/wavefront/wavefront_backtrace_buffer.h b/src/lib/wfa2/wavefront/wavefront_backtrace_buffer.h new file mode 100644 index 000000000..b2d543766 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_backtrace_buffer.h @@ -0,0 +1,186 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront backtrace buffer to store bactrace-blocks + */ + +#ifndef WAVEFRONT_BACKTRACE_BUFFER_H_ +#define WAVEFRONT_BACKTRACE_BUFFER_H_ + +#include "../alignment/cigar.h" +#include "../utils/commons.h" +#include "../utils/vector.h" +#include "../utils/bitmap.h" +#include "../system/mm_allocator.h" +#include "wavefront_pcigar.h" +#include "wavefront_offset.h" +#include "wavefront_attributes.h" + +/* + * Separated Backtrace Block + */ +typedef uint32_t bt_block_idx_t; // Up to 2^31 references (~32GB of not-compactable pCIGARs) +#define BT_BLOCK_IDX_MAX UINT32_MAX +#define BT_BLOCK_IDX_NULL UINT32_MAX + +typedef struct { + pcigar_t pcigar; // Packed CIGAR + bt_block_idx_t prev_idx; // Index of the previous BT-block +} __attribute__((packed)) bt_block_t; + +/* + * Backtrace initial positions + */ +typedef struct { + int v; + int h; +} wf_backtrace_init_pos_t; + +/* + * Backtrace Buffer + */ +typedef struct { + // Locator + int segment_idx; // Current segment idx + int segment_offset; // Current free position within segment + bt_block_t* block_next; // Next BT-block free + // Buffers + vector_t* segments; // Memory segments (bt_block_t*) + vector_t* alignment_init_pos; // Buffer to store alignment's initial coordinates (h,v) (wf_backtrace_init_pos_t) + bt_block_idx_t num_compacted_blocks; // Total compacted blocks in BT-buffer compacted (dense from 0..num_compacted_blocks-1) + int num_compactions; // Total compactions performed + // Internal buffers + vector_t* alignment_packed; // Temporal buffer to store final alignment (pcigar_t) + vector_t* prefetch_blocks_idxs; // Temporal buffer to store blocks_idxs (bt_block_idx_t) + // MM + mm_allocator_t* mm_allocator; +} wf_backtrace_buffer_t; + +/* + * Setup + */ +wf_backtrace_buffer_t* wf_backtrace_buffer_new( + mm_allocator_t* const mm_allocator); +void wf_backtrace_buffer_clear( + wf_backtrace_buffer_t* const bt_buffer); +void wf_backtrace_buffer_reap( + wf_backtrace_buffer_t* const bt_buffer); +void wf_backtrace_buffer_delete( + wf_backtrace_buffer_t* const bt_buffer); + +/* + * Accessors + */ +void wf_backtrace_buffer_add_used( + wf_backtrace_buffer_t* const bt_buffer, + const int used); +bt_block_idx_t wf_backtrace_buffer_get_mem( + wf_backtrace_buffer_t* const bt_buffer, + bt_block_t** const bt_block_mem, + int* const bt_blocks_available); + +/* + * Store blocks + */ +bt_block_idx_t wf_backtrace_buffer_init_block( + wf_backtrace_buffer_t* const bt_buffer, + const int v, + const int h); + +/* + * Unpack CIGAR + */ +bt_block_t* wf_backtrace_buffer_traceback_pcigar( + wf_backtrace_buffer_t* const bt_buffer, + bt_block_t* bt_block); +void wf_backtrace_buffer_unpack_cigar_linear( + wf_backtrace_buffer_t* const bt_buffer, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + alignment_match_funct_t const match_funct, + void* const match_funct_arguments, + const int begin_v, + const int begin_h, + const int end_v, + const int end_h, + cigar_t* const cigar); +void wf_backtrace_buffer_unpack_cigar_affine( + wf_backtrace_buffer_t* const bt_buffer, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + alignment_match_funct_t const match_funct, + void* const match_funct_arguments, + const int begin_v, + const int begin_h, + const int end_v, + const int end_h, + cigar_t* const cigar); + +/* + * Compact + */ +void wf_backtrace_buffer_mark_backtrace( + wf_backtrace_buffer_t* const bt_buffer, + const bt_block_idx_t bt_block_idx, + bitmap_t* const bitmap); +void wf_backtrace_buffer_mark_backtrace_batch( + wf_backtrace_buffer_t* const bt_buffer, + wf_offset_t* const offsets, + bt_block_idx_t* const bt_block_idxs, + const int num_block_idxs, + bitmap_t* const bitmap); + +bt_block_idx_t wf_backtrace_buffer_compact_marked( + wf_backtrace_buffer_t* const bt_buffer, + bitmap_t* const bitmap, + const int verbose); + +/* + * Utils + */ +uint64_t wf_backtrace_buffer_get_used( + wf_backtrace_buffer_t* const bt_buffer); + +bt_block_idx_t wf_backtrace_buffer_get_num_compacted_blocks( + wf_backtrace_buffer_t* const bt_buffer); +void wf_backtrace_buffer_set_num_compacted_blocks( + wf_backtrace_buffer_t* const bt_buffer, + const bt_block_idx_t num_compacted_blocks); +void wf_backtrace_buffer_reset_compaction( + wf_backtrace_buffer_t* const bt_buffer); + +uint64_t wf_backtrace_buffer_get_size_allocated( + wf_backtrace_buffer_t* const bt_buffer); +uint64_t wf_backtrace_buffer_get_size_used( + wf_backtrace_buffer_t* const bt_buffer); + +#endif /* WAVEFRONT_BACKTRACE_BUFFER_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_backtrace_offload.c b/src/lib/wfa2/wavefront/wavefront_backtrace_offload.c new file mode 100644 index 000000000..fe45f0821 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_backtrace_offload.c @@ -0,0 +1,288 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for offloading partial backtraces + */ + +#include "../utils/string_padded.h" +#include "wavefront_backtrace_offload.h" + +/* + * Backtrace-blocks offloading + */ +void wavefront_backtrace_offload_blocks_selective( + wf_offset_t* const out_offsets, + pcigar_t* const out_bt_pcigar, + bt_block_idx_t* const out_bt_prev, + const int lo, + const int hi, + const pcigar_t occupation_mask, + wf_backtrace_buffer_t* const bt_buffer) { + // Fetch BT-buffer free memory + int bt_blocks_available; + bt_block_t* bt_block_mem; + bt_block_idx_t global_pos = wf_backtrace_buffer_get_mem(bt_buffer,&bt_block_mem,&bt_blocks_available); + bt_block_idx_t current_pos = global_pos; + const int max_pos = current_pos + bt_blocks_available; + // Check PCIGAR buffers full and off-load if needed + int k; + for (k=lo;k<=hi;++k) { + if (out_offsets[k]>=0 && PCIGAR_IS_UTILISED(out_bt_pcigar[k],occupation_mask)) { + // Store + bt_block_mem->pcigar = out_bt_pcigar[k]; + bt_block_mem->prev_idx = out_bt_prev[k]; + bt_block_mem++; + // Reset + out_bt_pcigar[k] = 0; + out_bt_prev[k] = current_pos; + current_pos++; + // Update pos + if (current_pos >= max_pos) { + wf_backtrace_buffer_add_used(bt_buffer,current_pos-global_pos); + global_pos = wf_backtrace_buffer_get_mem(bt_buffer,&bt_block_mem,&bt_blocks_available); + } + } + } + wf_backtrace_buffer_add_used(bt_buffer,current_pos-global_pos); +} +/* + * Backtrace offloading (linear) + */ +int wavefront_backtrace_offload_blocks_linear( + wavefront_aligner_t* const wf_aligner, + wf_offset_t* const out_offsets, + pcigar_t* const out_bt_pcigar, + bt_block_idx_t* const out_bt_prev, + const int lo, + const int hi) { + // Parameters + const wavefront_memory_t wavefront_memory = wf_aligner->memory_mode; + wf_backtrace_buffer_t* const bt_buffer = wf_aligner->wf_components.bt_buffer; + // Select memory-mode + switch (wavefront_memory) { + case wavefront_memory_med: + wavefront_backtrace_offload_blocks_selective( + out_offsets,out_bt_pcigar,out_bt_prev, + lo,hi,PCIGAR_HALF_FULL_MASK,bt_buffer); + return PCIGAR_MAX_LENGTH/2; // Half occupancy + break; + case wavefront_memory_low: + wavefront_backtrace_offload_blocks_selective( + out_offsets,out_bt_pcigar,out_bt_prev, + lo,hi,PCIGAR_FULL_MASK,bt_buffer); + return PCIGAR_MAX_LENGTH-1; // At least 1-slots free + break; + default: + fprintf(stderr,"[WFA::compute] Wrong memory-mode\n"); + exit(1); + } +} +void wavefront_backtrace_offload_linear( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Paramters + wavefront_t* const wf_m = wavefront_set->out_mwavefront; + const wavefront_t* const m_misms = wavefront_set->in_mwavefront_misms; + const wavefront_t* const m_open1 = wavefront_set->in_mwavefront_open1; + // Compute BT occupancy maximum + int occ_max_m = 0, occ_max_indel = 0; + if (!m_open1->null) occ_max_indel = m_open1->bt_occupancy_max; + if (!m_misms->null) occ_max_m = m_misms->bt_occupancy_max; + const int occ_max = MAX(occ_max_indel,occ_max_m) + 1; + // Set new occupancy + wf_m->bt_occupancy_max = occ_max; + // Offload if necessary (Gap-Linear) + if (!wf_m->null && occ_max >= PCIGAR_MAX_LENGTH) { + wf_offset_t* const out_m = wavefront_set->out_mwavefront->offsets; + pcigar_t* const out_m_bt_pcigar = wavefront_set->out_mwavefront->bt_pcigar; + bt_block_idx_t* const out_m_bt_prev = wavefront_set->out_mwavefront->bt_prev; + wavefront_set->out_mwavefront->bt_occupancy_max = + wavefront_backtrace_offload_blocks_linear( + wf_aligner,out_m,out_m_bt_pcigar,out_m_bt_prev,lo,hi); + } +} +/* + * Backtrace offloading (gap-affine) + */ +int wavefront_backtrace_offload_blocks_affine( + wavefront_aligner_t* const wf_aligner, + wf_offset_t* const out_offsets, + pcigar_t* const out_bt_pcigar, + bt_block_idx_t* const out_bt_prev, + const int lo, + const int hi) { + // Parameters + const wavefront_memory_t wavefront_memory = wf_aligner->memory_mode; + wf_backtrace_buffer_t* const bt_buffer = wf_aligner->wf_components.bt_buffer; + // Select memory-mode + switch (wavefront_memory) { + case wavefront_memory_med: + wavefront_backtrace_offload_blocks_selective( + out_offsets,out_bt_pcigar,out_bt_prev, + lo,hi,PCIGAR_HALF_FULL_MASK,bt_buffer); + return PCIGAR_MAX_LENGTH/2; // Half occupancy + case wavefront_memory_low: + wavefront_backtrace_offload_blocks_selective( + out_offsets,out_bt_pcigar,out_bt_prev, + lo,hi,PCIGAR_ALMOST_FULL_MASK,bt_buffer); + return PCIGAR_MAX_LENGTH-2; // At least 2-slots free + default: + fprintf(stderr,"[WFA::compute] Wrong memory-mode\n"); + exit(1); + return 0; + } +} +void wavefront_backtrace_offload_occupation_affine( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set) { + // Parameters + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + // Select distance metric + int occ_max_m = 0; + int occ_max_i1 = 0, occ_max_i2 = 0; + int occ_max_d1 = 0, occ_max_d2 = 0; + if (distance_metric == gap_affine) { + // Parameters + const wavefront_t* const m_misms = wavefront_set->in_mwavefront_misms; + const wavefront_t* const m_open1 = wavefront_set->in_mwavefront_open1; + const wavefront_t* const i1_ext = wavefront_set->in_i1wavefront_ext; + const wavefront_t* const d1_ext = wavefront_set->in_d1wavefront_ext; + // Compute BT occupancy maximum + if (!m_open1->null) { + occ_max_i1 = m_open1->bt_occupancy_max + 1; + occ_max_d1 = m_open1->bt_occupancy_max + 1; + } + if (!i1_ext->null) occ_max_i1 = MAX(occ_max_i1,i1_ext->bt_occupancy_max+1); + if (!d1_ext->null) occ_max_d1 = MAX(occ_max_d1,d1_ext->bt_occupancy_max+1); + if (!m_misms->null) occ_max_m = m_misms->bt_occupancy_max; + if (occ_max_m < occ_max_i1) occ_max_m = occ_max_i1; + if (occ_max_m < occ_max_d1) occ_max_m = occ_max_d1; + ++occ_max_m; + // Set new occupancy + wavefront_set->out_i1wavefront->bt_occupancy_max = occ_max_i1; + wavefront_set->out_d1wavefront->bt_occupancy_max = occ_max_d1; + wavefront_set->out_mwavefront->bt_occupancy_max = occ_max_m; + } else { // distance_metric == gap_affine_2p + // Parameters + const wavefront_t* const m_misms = wavefront_set->in_mwavefront_misms; + const wavefront_t* const m_open1 = wavefront_set->in_mwavefront_open1; + const wavefront_t* const m_open2 = wavefront_set->in_mwavefront_open2; + const wavefront_t* const i1_ext = wavefront_set->in_i1wavefront_ext; + const wavefront_t* const i2_ext = wavefront_set->in_i2wavefront_ext; + const wavefront_t* const d1_ext = wavefront_set->in_d1wavefront_ext; + const wavefront_t* const d2_ext = wavefront_set->in_d2wavefront_ext; + // Compute BT occupancy maximum (I) + if (!m_open1->null) { + occ_max_i1 = m_open1->bt_occupancy_max + 1; + occ_max_d1 = m_open1->bt_occupancy_max + 1; + } + // Compute BT occupancy maximum (D) + if (!i1_ext->null) occ_max_i1 = MAX(occ_max_i1,i1_ext->bt_occupancy_max+1); + if (!d1_ext->null) occ_max_d1 = MAX(occ_max_d1,d1_ext->bt_occupancy_max+1); + if (!m_open2->null) { + occ_max_i2 = m_open2->bt_occupancy_max + 1; + occ_max_d2 = m_open2->bt_occupancy_max + 1; + } + if (!i2_ext->null) occ_max_i2 = MAX(occ_max_i2,i2_ext->bt_occupancy_max+1); + if (!d2_ext->null) occ_max_d2 = MAX(occ_max_d2,d2_ext->bt_occupancy_max+1); + // Compute BT occupancy maximum (M) + if (!m_misms->null) occ_max_m = m_misms->bt_occupancy_max; + if (occ_max_m < occ_max_i1) occ_max_m = occ_max_i1; + if (occ_max_m < occ_max_i2) occ_max_m = occ_max_i2; + if (occ_max_m < occ_max_d1) occ_max_m = occ_max_d1; + if (occ_max_m < occ_max_d2) occ_max_m = occ_max_d2; + ++occ_max_m; + // Set new occupancy + wavefront_set->out_i1wavefront->bt_occupancy_max = occ_max_i1; + wavefront_set->out_i2wavefront->bt_occupancy_max = occ_max_i2; + wavefront_set->out_d1wavefront->bt_occupancy_max = occ_max_d1; + wavefront_set->out_d2wavefront->bt_occupancy_max = occ_max_d2; + wavefront_set->out_mwavefront->bt_occupancy_max = occ_max_m; + } +} +void wavefront_backtrace_offload_affine( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Parameters + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + // Compute maximum occupancy + wavefront_backtrace_offload_occupation_affine(wf_aligner,wavefront_set); + // Offload if necessary (Gap-Affine) + const wavefront_t* const wf_m = wavefront_set->out_mwavefront; + if (!wf_m->null && wf_m->bt_occupancy_max >= PCIGAR_MAX_LENGTH-1) { + wf_offset_t* const out_m = wavefront_set->out_mwavefront->offsets; + pcigar_t* const out_m_bt_pcigar = wavefront_set->out_mwavefront->bt_pcigar; + bt_block_idx_t* const out_m_bt_prev = wavefront_set->out_mwavefront->bt_prev; + wavefront_set->out_mwavefront->bt_occupancy_max = + wavefront_backtrace_offload_blocks_affine( + wf_aligner,out_m,out_m_bt_pcigar,out_m_bt_prev,lo,hi); + } + const wavefront_t* const wf_i1 = wavefront_set->out_i1wavefront; + if (!wf_i1->null && wf_i1->bt_occupancy_max >= PCIGAR_MAX_LENGTH-1) { + wf_offset_t* const out_i1 = wavefront_set->out_i1wavefront->offsets; + pcigar_t* const out_i1_bt_pcigar = wavefront_set->out_i1wavefront->bt_pcigar; + bt_block_idx_t* const out_i1_bt_prev = wavefront_set->out_i1wavefront->bt_prev; + wavefront_set->out_i1wavefront->bt_occupancy_max = + wavefront_backtrace_offload_blocks_affine( + wf_aligner,out_i1,out_i1_bt_pcigar,out_i1_bt_prev,lo,hi); + } + const wavefront_t* const wf_d1 = wavefront_set->out_d1wavefront; + if (!wf_d1->null && wf_d1->bt_occupancy_max >= PCIGAR_MAX_LENGTH-1) { + wf_offset_t* const out_d1 = wavefront_set->out_d1wavefront->offsets; + pcigar_t* const out_d1_bt_pcigar = wavefront_set->out_d1wavefront->bt_pcigar; + bt_block_idx_t* const out_d1_bt_prev = wavefront_set->out_d1wavefront->bt_prev; + wavefront_set->out_d1wavefront->bt_occupancy_max = + wavefront_backtrace_offload_blocks_affine( + wf_aligner,out_d1,out_d1_bt_pcigar,out_d1_bt_prev,lo,hi); + } + if (distance_metric == gap_affine) return; + // Offload if necessary (Gap-Affine-2p) + const wavefront_t* const wf_i2 = wavefront_set->out_i2wavefront; + if (!wf_i2->null && wf_i2->bt_occupancy_max >= PCIGAR_MAX_LENGTH-1) { + wf_offset_t* const out_i2 = wavefront_set->out_i2wavefront->offsets; + pcigar_t* const out_i2_bt_pcigar = wavefront_set->out_i2wavefront->bt_pcigar; + bt_block_idx_t* const out_i2_bt_prev = wavefront_set->out_i2wavefront->bt_prev; + wavefront_set->out_i2wavefront->bt_occupancy_max = + wavefront_backtrace_offload_blocks_affine( + wf_aligner,out_i2,out_i2_bt_pcigar,out_i2_bt_prev,lo,hi); + } + const wavefront_t* const wf_d2 = wavefront_set->out_d2wavefront; + if (!wf_d2->null && wf_d2->bt_occupancy_max >= PCIGAR_MAX_LENGTH-1) { + wf_offset_t* const out_d2 = wavefront_set->out_d2wavefront->offsets; + pcigar_t* const out_d2_bt_pcigar = wavefront_set->out_d2wavefront->bt_pcigar; + bt_block_idx_t* const out_d2_bt_prev = wavefront_set->out_d2wavefront->bt_prev; + wavefront_set->out_d2wavefront->bt_occupancy_max = + wavefront_backtrace_offload_blocks_affine( + wf_aligner,out_d2,out_d2_bt_pcigar,out_d2_bt_prev,lo,hi); + } +} + diff --git a/src/lib/wfa2/wavefront/wavefront_backtrace_offload.h b/src/lib/wfa2/wavefront/wavefront_backtrace_offload.h new file mode 100644 index 000000000..96ef3c764 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_backtrace_offload.h @@ -0,0 +1,69 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for offloading partial backtraces + */ + +#ifndef WAVEFRONT_BACKTRACE_OFFLOAD_H_ +#define WAVEFRONT_BACKTRACE_OFFLOAD_H_ + +#include "wavefront_aligner.h" + +/* + * Backtrace offloading (gap-linear) + */ +int wavefront_backtrace_offload_blocks_linear( + wavefront_aligner_t* const wf_aligner, + wf_offset_t* const out_offsets, + pcigar_t* const out_bt_pcigar, + bt_block_idx_t* const out_bt_prev, + const int lo, + const int hi); +void wavefront_backtrace_offload_linear( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi); + +/* + * Backtrace offloading (gap-affine) + */ +int wavefront_backtrace_offload_blocks_affine( + wavefront_aligner_t* const wf_aligner, + wf_offset_t* const out_offsets, + pcigar_t* const out_bt_pcigar, + bt_block_idx_t* const out_bt_prev, + const int lo, + const int hi); +void wavefront_backtrace_offload_affine( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi); + +#endif /* WAVEFRONT_BACKTRACE_OFFLOAD_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_bialign.c b/src/lib/wfa2/wavefront/wavefront_bialign.c new file mode 100644 index 000000000..98803227e --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_bialign.c @@ -0,0 +1,746 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + */ + +#include "wavefront_bialign.h" +#include "wavefront_unialign.h" +#include "wavefront_bialigner.h" + +#include "wavefront_compute.h" +#include "wavefront_compute_affine.h" +#include "wavefront_compute_affine2p.h" +#include "wavefront_compute_edit.h" +#include "wavefront_compute_linear.h" +#include "wavefront_extend.h" +#include "wavefront_plot.h" +#include "wavefront_debug.h" + +/* + * Config + */ +#define WF_BIALIGN_FALLBACK_MIN_SCORE 250 +#define WF_BIALIGN_FALLBACK_MIN_LENGTH 100 + +/* + * Debug + */ +void wavefront_bialign_debug( + wf_bialign_breakpoint_t* const breakpoint, + const int align_level) { + // Parameters + const int breakpoint_h = WAVEFRONT_H(breakpoint->k_forward,breakpoint->offset_forward); + const int breakpoint_v = WAVEFRONT_V(breakpoint->k_forward,breakpoint->offset_forward); + // Prinf debug info + fprintf(stderr,"[WFA::BiAlign][Recursion=%d] ",align_level); + int i; for (i=0;iscore); + switch (breakpoint->component) { + case affine2p_matrix_M: fprintf(stderr,"M"); break; + case affine2p_matrix_I1: fprintf(stderr,"I1"); break; + case affine2p_matrix_I2: fprintf(stderr,"I2"); break; + case affine2p_matrix_D1: fprintf(stderr,"D1"); break; + case affine2p_matrix_D2: fprintf(stderr,"D2"); break; + default: fprintf(stderr,"?"); break; + } + fprintf(stderr,")\n"); +} +/* + * Bidirectional check breakpoints + */ +void wavefront_bialign_breakpoint_indel2indel( + wavefront_aligner_t* const wf_aligner, + const bool breakpoint_forward, + const int score_0, + const int score_1, + wavefront_t* const dwf_0, + wavefront_t* const dwf_1, + const affine2p_matrix_type component, + wf_bialign_breakpoint_t* const breakpoint) { + // Parameters + const int text_length = wf_aligner->text_length; + const int pattern_length = wf_aligner->pattern_length; + const int gap_open = + (component==affine2p_matrix_I1 || component==affine2p_matrix_D1) ? + wf_aligner->penalties.gap_opening1 : wf_aligner->penalties.gap_opening2; + // Check wavefronts overlapping + const int lo_0 = dwf_0->lo; + const int hi_0 = dwf_0->hi; + const int lo_1 = WAVEFRONT_K_INVERSE(dwf_1->hi,pattern_length,text_length); + const int hi_1 = WAVEFRONT_K_INVERSE(dwf_1->lo,pattern_length,text_length); + if (hi_1 < lo_0 || hi_0 < lo_1) return; + // Compute overlapping interval + const int min_hi = MIN(hi_0,hi_1); + const int max_lo = MAX(lo_0,lo_1); + int k_0; + for (k_0=max_lo;k_0<=min_hi;k_0++) { + const int k_1 = WAVEFRONT_K_INVERSE(k_0,pattern_length,text_length); + // Fetch offsets + const wf_offset_t doffset_0 = dwf_0->offsets[k_0]; + const wf_offset_t doffset_1 = dwf_1->offsets[k_1]; + const int dh_0 = WAVEFRONT_H(k_0,doffset_0); + const int dh_1 = WAVEFRONT_H(k_1,doffset_1); + // Check breakpoint d2d + if (dh_0 + dh_1 >= text_length && score_0 + score_1 - gap_open < breakpoint->score) { + if (breakpoint_forward) { + breakpoint->score_forward = score_0; + breakpoint->score_reverse = score_1; + breakpoint->k_forward = k_0; + breakpoint->k_reverse = k_1; + breakpoint->offset_forward = dh_0; + breakpoint->offset_reverse = dh_1; + } else { + breakpoint->score_forward = score_1; + breakpoint->score_reverse = score_0; + breakpoint->k_forward = k_1; + breakpoint->k_reverse = k_0; + breakpoint->offset_forward = dh_1; + breakpoint->offset_reverse = dh_0; + } + breakpoint->score = score_0 + score_1 - gap_open; + breakpoint->component = component; + // wavefront_bialign_debug(breakpoint,-1); // DEBUG + // No need to keep searching + return; + } + } +} +void wavefront_bialign_breakpoint_m2m( + wavefront_aligner_t* const wf_aligner, + const bool breakpoint_forward, + const int score_0, + const int score_1, + wavefront_t* const mwf_0, + wavefront_t* const mwf_1, + wf_bialign_breakpoint_t* const breakpoint) { + // Parameters + const int text_length = wf_aligner->text_length; + const int pattern_length = wf_aligner->pattern_length; + // Check wavefronts overlapping + const int lo_0 = mwf_0->lo; + const int hi_0 = mwf_0->hi; + const int lo_1 = WAVEFRONT_K_INVERSE(mwf_1->hi,pattern_length,text_length); + const int hi_1 = WAVEFRONT_K_INVERSE(mwf_1->lo,pattern_length,text_length); + if (hi_1 < lo_0 || hi_0 < lo_1) return; + // Compute overlapping interval + const int min_hi = MIN(hi_0,hi_1); + const int max_lo = MAX(lo_0,lo_1); + int k_0; + for (k_0=max_lo;k_0<=min_hi;k_0++) { + const int k_1 = WAVEFRONT_K_INVERSE(k_0,pattern_length,text_length); + // Fetch offsets + const wf_offset_t moffset_0 = mwf_0->offsets[k_0]; + const wf_offset_t moffset_1 = mwf_1->offsets[k_1]; + const int mh_0 = WAVEFRONT_H(k_0,moffset_0); + const int mh_1 = WAVEFRONT_H(k_1,moffset_1); + // Check breakpoint m2m + if (mh_0 + mh_1 >= text_length && score_0 + score_1 < breakpoint->score) { + if (breakpoint_forward) { + breakpoint->score_forward = score_0; + breakpoint->score_reverse = score_1; + breakpoint->k_forward = k_0; + breakpoint->k_reverse = k_1; + breakpoint->offset_forward = moffset_0; + breakpoint->offset_reverse = moffset_1; + } else { + breakpoint->score_forward = score_1; + breakpoint->score_reverse = score_0; + breakpoint->k_forward = k_1; + breakpoint->k_reverse = k_0; + breakpoint->offset_forward = moffset_1; + breakpoint->offset_reverse = moffset_0; + } + breakpoint->score = score_0 + score_1; + breakpoint->component = affine2p_matrix_M; + // wavefront_bialign_debug(breakpoint,-1); // DEBUG + // No need to keep searching + return; + } + } +} +/* + * Bidirectional find overlaps + */ +void wavefront_bialign_overlap( + wavefront_aligner_t* const wf_aligner_0, + wavefront_aligner_t* const wf_aligner_1, + const int score_0, + const int score_1, + const bool breakpoint_forward, + wf_bialign_breakpoint_t* const breakpoint) { + // Parameters + const int max_score_scope = wf_aligner_0->wf_components.max_score_scope; + const distance_metric_t distance_metric = wf_aligner_0->penalties.distance_metric; + const int gap_opening1 = wf_aligner_0->penalties.gap_opening1; + const int gap_opening2 = wf_aligner_0->penalties.gap_opening2; + // Fetch wavefronts-0 + const int score_mod_0 = score_0 % max_score_scope; + wavefront_t* const mwf_0 = wf_aligner_0->wf_components.mwavefronts[score_mod_0]; + if (mwf_0 == NULL) return; + wavefront_t* d1wf_0 = NULL, *i1wf_0 = NULL; + if (distance_metric >= gap_affine) { + d1wf_0 = wf_aligner_0->wf_components.d1wavefronts[score_mod_0]; + i1wf_0 = wf_aligner_0->wf_components.i1wavefronts[score_mod_0]; + } + wavefront_t* d2wf_0 = NULL, *i2wf_0 = NULL; + if (distance_metric == gap_affine_2p) { + d2wf_0 = wf_aligner_0->wf_components.d2wavefronts[score_mod_0]; + i2wf_0 = wf_aligner_0->wf_components.i2wavefronts[score_mod_0]; + } + // Traverse all scores-1 + int i; + for (i=0;i= breakpoint->score) continue; + // Check breakpoint d2d + wavefront_t* const d2wf_1 = wf_aligner_1->wf_components.d2wavefronts[score_mod_i]; + if (d2wf_0 != NULL && d2wf_1 != NULL) { + wavefront_bialign_breakpoint_indel2indel( + wf_aligner_0,breakpoint_forward,score_0,score_i, + d2wf_0,d2wf_1,affine2p_matrix_D2,breakpoint); + } + // Check breakpoint i2i + wavefront_t* const i2wf_1 = wf_aligner_1->wf_components.i2wavefronts[score_mod_i]; + if (i2wf_0 != NULL && i2wf_1 != NULL) { + wavefront_bialign_breakpoint_indel2indel( + wf_aligner_0,breakpoint_forward,score_0,score_i, + i2wf_0,i2wf_1,affine2p_matrix_I2,breakpoint); + } + } + // Check I1/D1-breakpoints (gap_affine) + if (distance_metric >= gap_affine) { + if (score_0 + score_i - gap_opening1 >= breakpoint->score) continue; + // Check breakpoint d2d + wavefront_t* const d1wf_1 = wf_aligner_1->wf_components.d1wavefronts[score_mod_i]; + if (d1wf_0 != NULL && d1wf_1 != NULL) { + wavefront_bialign_breakpoint_indel2indel( + wf_aligner_0,breakpoint_forward,score_0,score_i, + d1wf_0,d1wf_1,affine2p_matrix_D1,breakpoint); + } + // Check breakpoint i2i + wavefront_t* const i1wf_1 = wf_aligner_1->wf_components.i1wavefronts[score_mod_i]; + if (i1wf_0 != NULL && i1wf_1 != NULL) { + wavefront_bialign_breakpoint_indel2indel( + wf_aligner_0,breakpoint_forward,score_0,score_i, + i1wf_0,i1wf_1,affine2p_matrix_I1,breakpoint); + } + } + // Check M-breakpoints (indel, edit, gap-linear) + if (score_0 + score_i >= breakpoint->score) continue; + wavefront_t* const mwf_1 = wf_aligner_1->wf_components.mwavefronts[score_mod_i]; + if (mwf_1 != NULL) { + wavefront_bialign_breakpoint_m2m( + wf_aligner_0,breakpoint_forward, + score_0,score_i,mwf_0,mwf_1,breakpoint); + } + } +} +/* + * Bidirectional breakpoint detection + */ +void wavefront_bialign_find_breakpoint_init( + wavefront_aligner_t* const alg_forward, + wavefront_aligner_t* const alg_reverse, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + const distance_metric_t distance_metric, + alignment_form_t* const form, + const affine2p_matrix_type component_begin, + const affine2p_matrix_type component_end) { + // Resize wavefront aligner + wavefront_unialign_resize(alg_forward,pattern,pattern_length,text,text_length,false); + wavefront_unialign_resize(alg_reverse,pattern,pattern_length,text,text_length,true); + // Configure form forward and reverse + alignment_span_t span_forward = + (form->pattern_begin_free > 0 || form->text_begin_free > 0) ? alignment_endsfree : alignment_end2end; + alignment_form_t form_forward = { + .span = span_forward, + .pattern_begin_free = form->pattern_begin_free, + .pattern_end_free = 0, + .text_begin_free = form->text_begin_free, + .text_end_free = 0, + }; + alignment_span_t span_reverse = + (form->pattern_end_free > 0 || form->text_end_free > 0) ? alignment_endsfree : alignment_end2end; + alignment_form_t form_reverse = { + .span = span_reverse, + .pattern_begin_free = form->pattern_end_free, + .pattern_end_free = 0, + .text_begin_free = form->text_end_free, + .text_end_free = 0, + }; + // Configure WF-compute function (global) + switch (distance_metric) { + case indel: + case edit: + alg_forward->align_status.wf_align_compute = &wavefront_compute_edit; + break; + case gap_linear: + alg_forward->align_status.wf_align_compute = &wavefront_compute_linear; + break; + case gap_affine: + alg_forward->align_status.wf_align_compute = &wavefront_compute_affine; + break; + case gap_affine_2p: + alg_forward->align_status.wf_align_compute = &wavefront_compute_affine2p; + break; + default: + fprintf(stderr,"[WFA] Distance function not implemented\n"); + exit(1); + break; + } + // Initialize wavefront (forward) + alg_forward->align_status.num_null_steps = 0; + alg_forward->alignment_form = form_forward; + alg_forward->component_begin = component_begin; + alg_forward->component_end = component_end; + wavefront_unialign_initialize_wavefronts(alg_forward,pattern_length,text_length); + // Initialize wavefront (reverse) + alg_reverse->align_status.num_null_steps = 0; + alg_reverse->alignment_form = form_reverse; + alg_reverse->component_begin = component_end; + alg_reverse->component_end = component_begin; + wavefront_unialign_initialize_wavefronts(alg_reverse,pattern_length,text_length); +} +int wavefront_bialign_overlap_gopen_adjust( + wavefront_aligner_t* const wf_aligner, + const distance_metric_t distance_metric) { + switch (distance_metric) { + case gap_affine: + return wf_aligner->penalties.gap_opening1; + case gap_affine_2p: + return MAX(wf_aligner->penalties.gap_opening1,wf_aligner->penalties.gap_opening2); + case indel: + case edit: + case gap_linear: + default: + return 0; + } +} +int wavefront_bialign_find_breakpoint( + wavefront_bialigner_t* const bialigner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + const distance_metric_t distance_metric, + alignment_form_t* const form, + const affine2p_matrix_type component_begin, + const affine2p_matrix_type component_end, + wf_bialign_breakpoint_t* const breakpoint, + const int align_level) { + // Parameters + wavefront_aligner_t* const alg_forward = bialigner->alg_forward; + wavefront_aligner_t* const alg_reverse = bialigner->alg_reverse; + // Init bialignment + wavefront_bialign_find_breakpoint_init( + alg_forward,alg_reverse, + pattern,pattern_length,text,text_length, + distance_metric,form,component_begin,component_end); + // DEBUG + alignment_system_t* const system = &alg_forward->system; + const int verbose = system->verbose; + if (verbose >= 2) { + wavefront_debug_prologue(alg_forward,pattern,pattern_length,text,text_length); + wavefront_debug_prologue(alg_reverse,pattern,pattern_length,text,text_length); + } + // Parameters + const int max_alignment_score = alg_forward->system.max_alignment_score; + const int max_antidiagonal = DPMATRIX_ANTIDIAGONAL(pattern_length,text_length) - 1; // Note: Even removing -1 + void (*wf_align_compute)(wavefront_aligner_t* const,const int) = alg_forward->align_status.wf_align_compute; + int score_forward = 0, score_reverse = 0, forward_max_ak = 0, reverse_max_ak = 0; + bool end_reached; + // Plot + const bool plot_enabled = (alg_forward->plot != NULL); + if (plot_enabled) { + wavefront_plot(alg_forward,0,align_level); + wavefront_plot(alg_reverse,0,align_level); + } + // Prepare and perform first bialignment step + breakpoint->score = INT_MAX; + end_reached = wavefront_extend_end2end_max(alg_forward,score_forward,&forward_max_ak); + if (end_reached) return alg_forward->align_status.status; + end_reached = wavefront_extend_end2end_max(alg_reverse,score_reverse,&reverse_max_ak); + if (end_reached) return alg_reverse->align_status.status; + // Compute wavefronts of increasing score until both wavefronts overlap + int max_ak = 0; + bool last_wf_forward; + while (true) { + // Check close-to-collision + if (forward_max_ak + reverse_max_ak >= max_antidiagonal) break; + /* + * Compute next wavefront (Forward) + */ + ++score_forward; + (*wf_align_compute)(alg_forward,score_forward); + if (plot_enabled) wavefront_plot(alg_forward,score_forward,align_level); // Plot + // Extend + end_reached = wavefront_extend_end2end_max(alg_forward,score_forward,&max_ak); + if (forward_max_ak < max_ak) forward_max_ak = max_ak; + last_wf_forward = true; + // Check end-reached and close-to-collision + if (end_reached) return alg_forward->align_status.status; + if (forward_max_ak + reverse_max_ak >= max_antidiagonal) break; + /* + * Compute next wavefront (Reverse) + */ + ++score_reverse; + (*wf_align_compute)(alg_reverse,score_reverse); + if (plot_enabled) wavefront_plot(alg_reverse,score_reverse,align_level); // Plot + // Extend + end_reached = wavefront_extend_end2end_max(alg_reverse,score_reverse,&max_ak); + if (reverse_max_ak < max_ak) reverse_max_ak = max_ak; + last_wf_forward = false; + // Check end-reached and max-score-reached + if (end_reached) return alg_reverse->align_status.status; + if (score_reverse + score_forward >= max_alignment_score) return WF_STATUS_MAX_SCORE_REACHED; + // DEBUG + if (verbose >= 3 && score_forward % system->probe_interval_global == 0) { + wavefront_unialign_print_status(stderr,alg_forward,score_forward); + } + } + // Advance until overlap is found + const int max_score_scope = alg_forward->wf_components.max_score_scope; + const int gap_opening = wavefront_bialign_overlap_gopen_adjust(alg_forward,distance_metric); + while (true) { + if (last_wf_forward) { + // Check overlapping wavefronts + const int min_score_reverse = (score_reverse > max_score_scope-1) ? score_reverse - (max_score_scope-1) : 0; + if (score_forward + min_score_reverse - gap_opening >= breakpoint->score) break; // Done! + wavefront_bialign_overlap(alg_forward,alg_reverse,score_forward,score_reverse,true,breakpoint); + /* + * Compute next wavefront (Reverse) + */ + ++score_reverse; + (*wf_align_compute)(alg_reverse,score_reverse); + if (plot_enabled) wavefront_plot(alg_reverse,score_reverse,align_level); // Plot + // Extend & check end-reached + end_reached = wavefront_extend_end2end(alg_reverse,score_reverse); + if (end_reached) return alg_reverse->align_status.status; + } + // Check overlapping wavefronts + const int min_score_forward = (score_forward > max_score_scope-1) ? score_forward - (max_score_scope-1) : 0; + if (min_score_forward + score_reverse - gap_opening >= breakpoint->score) break; // Done! + wavefront_bialign_overlap(alg_reverse,alg_forward,score_reverse,score_forward,false,breakpoint); + /* + * Compute next wavefront (Forward) + */ + ++score_forward; + (*wf_align_compute)(alg_forward,score_forward); + if (plot_enabled) wavefront_plot(alg_forward,score_forward,align_level); // Plot + // Extend & check end-reached/max-score-reached + end_reached = wavefront_extend_end2end(alg_forward,score_forward); + if (end_reached) return alg_forward->align_status.status; + if (score_reverse + score_forward >= max_alignment_score) return WF_STATUS_MAX_SCORE_REACHED; + // Enable always + last_wf_forward = true; + } + // Return OK + return WF_STATUS_SUCCESSFUL; +} +/* + * Bidirectional Alignment (base cases) + */ +void wavefront_bialign_base( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + alignment_form_t* const form, + const affine2p_matrix_type component_begin, + const affine2p_matrix_type component_end, + const int align_level) { + // Parameters + wavefront_aligner_t* const alg_subsidiary = wf_aligner->bialigner->alg_subsidiary; + const int verbose = wf_aligner->system.verbose; + // Configure + alg_subsidiary->alignment_form = *form; + wavefront_unialign_init( + alg_subsidiary,pattern,pattern_length, + text,text_length,component_begin,component_end); + // DEBUG + if (verbose >= 2) { + wavefront_debug_prologue(alg_subsidiary,pattern,pattern_length,text,text_length); + } + // Wavefront align sequences + wavefront_unialign(alg_subsidiary); + wf_aligner->align_status.status = alg_subsidiary->align_status.status; + // DEBUG + if (verbose >= 2) { + wavefront_debug_epilogue(alg_subsidiary); + wavefront_debug_check_correct(wf_aligner); + } + // Append CIGAR + cigar_append(wf_aligner->cigar,alg_subsidiary->cigar); + if (align_level == 0) wf_aligner->cigar->score = alg_subsidiary->cigar->score; +} +void wavefront_bialign_exception( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + alignment_form_t* const form, + const affine2p_matrix_type component_begin, + const affine2p_matrix_type component_end, + const int align_level, + const int align_status) { + // Check max-score reached or unfeasible alignment + if (align_status == WF_STATUS_MAX_SCORE_REACHED || + align_status == WF_STATUS_UNFEASIBLE) { + wf_aligner->align_status.status = align_status; + return; + } + // Check end reached + if (align_status == WF_STATUS_END_REACHED) { + wavefront_aligner_t* const alg_forward = wf_aligner->bialigner->alg_forward; + wavefront_aligner_t* const alg_reverse = wf_aligner->bialigner->alg_reverse; + // Retrieve score when end was reached + int score_reached; + if (alg_forward->align_status.status == WF_STATUS_END_REACHED) { + score_reached = alg_forward->align_status.score; + } else { + score_reached = alg_reverse->align_status.score; + } + // Fallback if possible + if (score_reached <= WF_BIALIGN_FALLBACK_MIN_SCORE) { + wavefront_bialign_base( + wf_aligner,pattern,pattern_length,text,text_length, + form,component_begin,component_end,align_level); + } else { + wf_aligner->align_status.status = WF_STATUS_UNFEASIBLE; + } + return; + } + // Otherwise + fprintf(stderr,"[WFA::BiAlign] Unknown condition\n"); + exit(1); +} +/* + * Bidirectional Alignment + */ +void wavefront_bialign_init_half_0( + alignment_form_t* const global_form, + alignment_form_t* const half_form) { + // Align half_0 + const alignment_span_t span_0 = + (global_form->pattern_begin_free > 0 || + global_form->text_begin_free > 0) ? + alignment_endsfree : alignment_end2end; + half_form->span = span_0; + half_form->pattern_begin_free = global_form->pattern_begin_free; + half_form->pattern_end_free = 0; + half_form->text_begin_free = global_form->text_begin_free; + half_form->text_end_free = 0; +} +void wavefront_bialign_init_half_1( + alignment_form_t* const global_form, + alignment_form_t* const half_form) { + // Align half_0 + const alignment_span_t span_1 = + (global_form->pattern_begin_free > 0 || + global_form->text_begin_free > 0) ? + alignment_endsfree : alignment_end2end; + half_form->span = span_1; + half_form->pattern_begin_free = 0; + half_form->pattern_end_free = global_form->pattern_end_free; + half_form->text_begin_free = 0; + half_form->text_end_free = global_form->text_end_free; +} +void wavefront_bialign_alignment( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_begin, + const int pattern_end, + const char* const text, + const int text_begin, + const int text_end, + alignment_form_t* const form, + const affine2p_matrix_type component_begin, + const affine2p_matrix_type component_end, + const int score_remaining, + const int align_level) { + // Parameters + const int pattern_length = pattern_end - pattern_begin; + const int text_length = text_end - text_begin; + // Trivial cases + if (text_length == 0) { + cigar_append_deletion(wf_aligner->cigar,pattern_length); + return; + } else if (pattern_length == 0) { + cigar_append_insertion(wf_aligner->cigar,text_length); + return; + } + // Fall back to regular WFA + if (score_remaining <= WF_BIALIGN_FALLBACK_MIN_SCORE) { + wavefront_bialign_base(wf_aligner, + pattern+pattern_begin,pattern_length, + text+text_begin,text_length, + form,component_begin,component_end,align_level); + return; + } + // Find breakpoint in the alignment + wf_bialign_breakpoint_t breakpoint; + const int align_status = wavefront_bialign_find_breakpoint( + wf_aligner->bialigner, + pattern+pattern_begin,pattern_length, + text+text_begin,text_length, + wf_aligner->penalties.distance_metric, + form,component_begin,component_end, + &breakpoint,align_level); + // DEBUG + if (wf_aligner->system.verbose >= 2) { + wavefront_debug_epilogue(wf_aligner->bialigner->alg_forward); + wavefront_debug_epilogue(wf_aligner->bialigner->alg_reverse); + } + // Check status + if (align_status != WF_STATUS_SUCCESSFUL) { + wavefront_bialign_exception(wf_aligner, + pattern+pattern_begin,pattern_length, + text+text_begin,text_length, + form,component_begin,component_end,align_level,align_status); + return; + } + // Breakpoint found + const int breakpoint_h = WAVEFRONT_H(breakpoint.k_forward,breakpoint.offset_forward); + const int breakpoint_v = WAVEFRONT_V(breakpoint.k_forward,breakpoint.offset_forward); + // DEBUG + if (wf_aligner->system.verbose >= 3) wavefront_bialign_debug(&breakpoint,align_level); + // Parameters + wavefront_plot_t* const plot = wf_aligner->plot; + // Align half_0 + alignment_form_t form_0; + if (plot) { + plot->offset_v = pattern_begin; + plot->offset_h = text_begin; + } + wavefront_bialign_init_half_0(form,&form_0); + wavefront_bialign_alignment(wf_aligner, + pattern,pattern_begin,pattern_begin+breakpoint_v, + text,text_begin,text_begin+breakpoint_h, + &form_0,component_begin,breakpoint.component, + breakpoint.score_forward,align_level+1); + if (wf_aligner->align_status.status != WF_STATUS_SUCCESSFUL) return; + // Align half_1 + alignment_form_t form_1; + if (plot) { + plot->offset_v = pattern_begin + breakpoint_v; + plot->offset_h = text_begin + breakpoint_h; + } + wavefront_bialign_init_half_1(form,&form_1); + wavefront_bialign_alignment(wf_aligner, + pattern,pattern_begin+breakpoint_v,pattern_end, + text,text_begin+breakpoint_h,text_end, + &form_1,breakpoint.component,component_end, + breakpoint.score_reverse,align_level+1); + if (wf_aligner->align_status.status != WF_STATUS_SUCCESSFUL) return; + // Set score + wf_aligner->cigar->score = wavefront_compute_classic_score( + wf_aligner,pattern_length,text_length,breakpoint.score); +} +/* + * Bidirectional Score-only + */ +void wavefront_bialign_compute_score( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length) { + // Find breakpoint in the alignment + wf_bialign_breakpoint_t breakpoint; + const int align_status = wavefront_bialign_find_breakpoint( + wf_aligner->bialigner,pattern,pattern_length,text,text_length, + wf_aligner->penalties.distance_metric,&wf_aligner->alignment_form, + affine_matrix_M,affine_matrix_M,&breakpoint,0); + // DEBUG + if (wf_aligner->system.verbose >= 2) { + wavefront_debug_epilogue(wf_aligner->bialigner->alg_forward); + wavefront_debug_epilogue(wf_aligner->bialigner->alg_reverse); + } + // Check status + if (align_status == WF_STATUS_MAX_SCORE_REACHED || align_status == WF_STATUS_UNFEASIBLE) { + wf_aligner->align_status.status = align_status; + return; + } + if (align_status == WF_STATUS_END_REACHED) { + wavefront_aligner_t* const alg_forward = wf_aligner->bialigner->alg_forward; + wavefront_aligner_t* const alg_reverse = wf_aligner->bialigner->alg_reverse; + if (alg_forward->align_status.status == WF_STATUS_END_REACHED) { + breakpoint.score = alg_forward->align_status.score; + } else { + breakpoint.score = alg_reverse->align_status.score; + } + } + // Report score + cigar_clear(wf_aligner->cigar); + wf_aligner->cigar->score = wavefront_compute_classic_score( + wf_aligner,pattern_length,text_length,breakpoint.score); + wf_aligner->align_status.status = WF_STATUS_SUCCESSFUL; +} +/* + * Bidirectional dispatcher + */ +void wavefront_bialign( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length) { + // Init + wf_aligner->align_status.status = WF_STATUS_SUCCESSFUL; // Init OK + // Just for outputting info at plot + wf_aligner->pattern = (char*)pattern; + wf_aligner->pattern_length = pattern_length; + wf_aligner->text = (char*)text; + wf_aligner->text_length = text_length; + // Select scope + if (wf_aligner->alignment_scope == compute_score) { + wavefront_bialign_compute_score(wf_aligner,pattern,pattern_length,text,text_length); + } else { + cigar_resize(wf_aligner->cigar,2*(pattern_length+text_length)); + // Bidirectional alignment + const bool min_length = MAX(pattern_length,text_length) <= WF_BIALIGN_FALLBACK_MIN_LENGTH; + wavefront_bialign_alignment(wf_aligner, + pattern,0,pattern_length, + text,0,text_length, + &wf_aligner->alignment_form, + affine_matrix_M,affine_matrix_M, + min_length ? 0 : INT_MAX,0); + } +} + diff --git a/src/lib/wfa2/wavefront/wavefront_bialign.h b/src/lib/wfa2/wavefront/wavefront_bialign.h new file mode 100644 index 000000000..969a1b7a6 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_bialign.h @@ -0,0 +1,47 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + */ + +#ifndef WAVEFRONT_BIALIGN_H_ +#define WAVEFRONT_BIALIGN_H_ + +#include "../utils/commons.h" +#include "wavefront_aligner.h" + +/* + * Bidirectional WFA + */ +void wavefront_bialign( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length); + +#endif /* WAVEFRONT_BIALIGN_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_bialigner.c b/src/lib/wfa2/wavefront/wavefront_bialigner.c new file mode 100644 index 000000000..bc052ce1c --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_bialigner.c @@ -0,0 +1,102 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + */ + +#include "wavefront_bialigner.h" +#include "wavefront_aligner.h" +#include "wavefront_attributes.h" +#include "wavefront_heuristic.h" + +/* + * Setup + */ +wavefront_bialigner_t* wavefront_bialigner_new( + wavefront_aligner_attr_t* const attributes, + wavefront_plot_t* const plot) { + // Allocate + wavefront_bialigner_t* const wf_bialigner = malloc(sizeof(wavefront_bialigner_t)); + // Configure subsidiary aligners + wavefront_aligner_attr_t subsidiary_attr = wavefront_aligner_attr_default; + // Inherit attributes from master aligner + subsidiary_attr.distance_metric = attributes->distance_metric; + subsidiary_attr.linear_penalties = attributes->linear_penalties; + subsidiary_attr.affine_penalties = attributes->affine_penalties; + subsidiary_attr.affine2p_penalties = attributes->affine2p_penalties; + subsidiary_attr.match_funct = attributes->match_funct; + subsidiary_attr.match_funct_arguments = attributes->match_funct_arguments; + // Set specifics for subsidiary aligners + subsidiary_attr.heuristic = attributes->heuristic; // Inherit same heuristic + subsidiary_attr.memory_mode = wavefront_memory_high; // Classic WFA + subsidiary_attr.alignment_scope = compute_score; + // Set other parameter for subsidiary aligners + subsidiary_attr.system = attributes->system; + // Allocate forward/reverse aligners + wf_bialigner->alg_forward = wavefront_aligner_new(&subsidiary_attr); + wf_bialigner->alg_forward->align_mode = wf_align_biwfa_breakpoint_forward; + wf_bialigner->alg_forward->plot = plot; + wf_bialigner->alg_reverse = wavefront_aligner_new(&subsidiary_attr); + wf_bialigner->alg_reverse->align_mode = wf_align_biwfa_breakpoint_reverse; + wf_bialigner->alg_reverse->plot = plot; + // Allocate subsidiary aligner + subsidiary_attr.alignment_scope = compute_alignment; + wf_bialigner->alg_subsidiary = wavefront_aligner_new(&subsidiary_attr); + wf_bialigner->alg_subsidiary->align_mode = wf_align_biwfa_subsidiary; + wf_bialigner->alg_subsidiary->plot = plot; + // Return + return wf_bialigner; +} +void wavefront_bialigner_reap( + wavefront_bialigner_t* const wf_bialigner) { + wavefront_aligner_reap(wf_bialigner->alg_forward); + wavefront_aligner_reap(wf_bialigner->alg_reverse); + wavefront_aligner_reap(wf_bialigner->alg_subsidiary); +} +void wavefront_bialigner_delete( + wavefront_bialigner_t* const wf_bialigner) { + wavefront_aligner_delete(wf_bialigner->alg_forward); + wavefront_aligner_delete(wf_bialigner->alg_reverse); + wavefront_aligner_delete(wf_bialigner->alg_subsidiary); + free(wf_bialigner); +} +/* + * Accessors + */ +uint64_t wavefront_bialigner_get_size( + wavefront_bialigner_t* const wf_bialigner) { + return wavefront_aligner_get_size(wf_bialigner->alg_forward) + + wavefront_aligner_get_size(wf_bialigner->alg_reverse) + + wavefront_aligner_get_size(wf_bialigner->alg_subsidiary); +} +void wavefront_bialigner_heuristic_inherit( + wavefront_bialigner_t* const wf_bialigner, + wavefront_heuristic_t* const heuristic) { + wf_bialigner->alg_forward->heuristic = *heuristic; + wf_bialigner->alg_reverse->heuristic = *heuristic; + wf_bialigner->alg_subsidiary->heuristic = *heuristic; +} diff --git a/src/lib/wfa2/wavefront/wavefront_bialigner.h b/src/lib/wfa2/wavefront/wavefront_bialigner.h new file mode 100644 index 000000000..4ac23a874 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_bialigner.h @@ -0,0 +1,82 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + */ + +#ifndef WAVEFRONT_BIALIGNER_H_ +#define WAVEFRONT_BIALIGNER_H_ + +#include "../utils/commons.h" +#include "wavefront_penalties.h" +#include "wavefront_attributes.h" +#include "wavefront_heuristic.h" +#include "wavefront_offset.h" + +// Wavefront ahead definition +typedef struct _wavefront_aligner_t wavefront_aligner_t; + +typedef struct { + // Scores + int score; // Score total + int score_forward; // Score (forward) + int score_reverse; // Score (reverse) + // Location + int k_forward; // Breakpoint diagonal (forward) + int k_reverse; // Breakpoint diagonal (reverse) + wf_offset_t offset_forward; // Offset (forward) + wf_offset_t offset_reverse; // Offset (reverse) + affine2p_matrix_type component; // Component (M/I/D) +} wf_bialign_breakpoint_t; + +typedef struct { + wavefront_aligner_t* alg_forward; // Forward aligner + wavefront_aligner_t* alg_reverse; // Reverse aligner + wavefront_aligner_t* alg_subsidiary; // Subsidiary aligner +} wavefront_bialigner_t; + +/* + * Setup + */ +wavefront_bialigner_t* wavefront_bialigner_new( + wavefront_aligner_attr_t* const attributes, + wavefront_plot_t* const plot); +void wavefront_bialigner_reap( + wavefront_bialigner_t* const wf_bialigner); +void wavefront_bialigner_delete( + wavefront_bialigner_t* const wf_bialigner); + +/* + * Accessors + */ +uint64_t wavefront_bialigner_get_size( + wavefront_bialigner_t* const wf_bialigner); +void wavefront_bialigner_heuristic_inherit( + wavefront_bialigner_t* const wf_bialigner, + wavefront_heuristic_t* const heuristic); + +#endif /* WAVEFRONT_BIALIGNER_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_components.c b/src/lib/wfa2/wavefront/wavefront_components.c new file mode 100644 index 000000000..b7a6eff7d --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_components.c @@ -0,0 +1,462 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront aligner components + */ + +#include "wavefront_components.h" +#include "../utils/bitmap.h" +#include "../system/profiler_timer.h" + +/* + * Configuration + */ +#define WF_NULL_INIT_LO (-1024) +#define WF_NULL_INIT_HI ( 1024) +#define WF_NULL_INIT_LENGTH WAVEFRONT_LENGTH(WF_NULL_INIT_LO,WF_NULL_INIT_HI) + +/* + * Compute dimensions + */ +void wavefront_components_dimensions_edit( + wavefront_components_t* const wf_components, + const int max_pattern_length, + const int max_text_length, + int* const max_score_scope, + int* const num_wavefronts) { + // Compute max-scope + *max_score_scope = 2; + // Dimensions + if (wf_components->memory_modular) { + *num_wavefronts = 2; + } else { + *num_wavefronts = MAX(max_pattern_length,max_text_length); + } +} +void wavefront_components_dimensions_linear( + wavefront_components_t* const wf_components, + wavefront_penalties_t* const penalties, + const int max_pattern_length, + const int max_text_length, + int* const max_score_scope, + int* const num_wavefronts) { + // Compute max-scope + *max_score_scope = MAX(penalties->mismatch,penalties->gap_opening1) + 1; + // Dimensions + if (wf_components->memory_modular) { + *num_wavefronts = *max_score_scope; + } else { + const int abs_seq_diff = ABS(max_pattern_length-max_text_length); + const int max_score_misms = MIN(max_pattern_length,max_text_length) * penalties->mismatch; + const int max_score_indel = penalties->gap_opening1 * abs_seq_diff; + *num_wavefronts = max_score_misms + max_score_indel; + } +} +void wavefront_components_dimensions_affine( + wavefront_components_t* const wf_components, + wavefront_penalties_t* const penalties, + const int max_pattern_length, + const int max_text_length, + int* const max_score_scope, + int* const num_wavefronts) { + // Compute max-scope + const int max_score_scope_indel = penalties->gap_opening1+penalties->gap_extension1; + *max_score_scope = MAX(max_score_scope_indel,penalties->mismatch) + 1; + // Dimensions + if (wf_components->memory_modular) { + *num_wavefronts = *max_score_scope; + } else { + const int abs_seq_diff = ABS(max_pattern_length-max_text_length); + const int max_score_misms = MIN(max_pattern_length,max_text_length) * penalties->mismatch; + const int max_score_indel = penalties->gap_opening1 + abs_seq_diff * penalties->gap_extension1; + *num_wavefronts = max_score_misms + max_score_indel; + } +} +void wavefront_components_dimensions_affine2p( + wavefront_components_t* const wf_components, + wavefront_penalties_t* const penalties, + const int max_pattern_length, + const int max_text_length, + int* const max_score_scope, + int* const num_wavefronts) { + // Compute max-scope + const int max_score_scope_indel = + MAX(penalties->gap_opening1+penalties->gap_extension1, + penalties->gap_opening2+penalties->gap_extension2); + *max_score_scope = MAX(max_score_scope_indel,penalties->mismatch) + 1; + // Dimensions + if (wf_components->memory_modular) { + *num_wavefronts = *max_score_scope; + } else { + const int abs_seq_diff = ABS(max_pattern_length-max_text_length); + const int max_score_misms = MIN(max_pattern_length,max_text_length) * penalties->mismatch; + const int max_score_indel1 = penalties->gap_opening1 + abs_seq_diff * penalties->gap_extension1; + const int max_score_indel2 = penalties->gap_opening2 + abs_seq_diff * penalties->gap_extension2; + const int max_score_indel = MIN(max_score_indel1,max_score_indel2); + *num_wavefronts = max_score_misms + max_score_indel; + } +} +void wavefront_components_dimensions( + wavefront_components_t* const wf_components, + wavefront_penalties_t* const penalties, + const int max_pattern_length, + const int max_text_length, + int* const max_score_scope, + int* const num_wavefronts) { + // Switch attending to distance-metric + switch (penalties->distance_metric) { + case indel: + case edit: + wavefront_components_dimensions_edit( + wf_components, + max_pattern_length,max_text_length, + max_score_scope,num_wavefronts); + break; + case gap_linear: + wavefront_components_dimensions_linear( + wf_components,penalties, + max_pattern_length,max_text_length, + max_score_scope,num_wavefronts); + break; + case gap_affine: + wavefront_components_dimensions_affine( + wf_components,penalties, + max_pattern_length,max_text_length, + max_score_scope,num_wavefronts); + break; + case gap_affine_2p: + wavefront_components_dimensions_affine2p( + wf_components,penalties, + max_pattern_length,max_text_length, + max_score_scope,num_wavefronts); + break; + } + // Clear historic + wf_components->historic_max_hi = 0; + wf_components->historic_min_lo = 0; +} +/* + * Setup + */ +void wavefront_components_allocate_wf( + wavefront_components_t* const wf_components, + const int max_pattern_length, + const int max_text_length, + const distance_metric_t distance_metric) { + // Parameters + const int num_wavefronts = wf_components->num_wavefronts; + const bool init_wf = wf_components->memory_modular; + mm_allocator_t* const mm_allocator = wf_components->mm_allocator; + // Allocate wavefronts + wf_components->mwavefronts = mm_allocator_calloc(mm_allocator,num_wavefronts,wavefront_t*,init_wf); + if (distance_metric <= gap_linear) { + wf_components->i1wavefronts = NULL; + wf_components->d1wavefronts = NULL; + wf_components->i2wavefronts = NULL; + wf_components->d2wavefronts = NULL; + } else { + wf_components->i1wavefronts = mm_allocator_calloc(mm_allocator,num_wavefronts,wavefront_t*,init_wf); + wf_components->d1wavefronts = mm_allocator_calloc(mm_allocator,num_wavefronts,wavefront_t*,init_wf); + if (distance_metric == gap_affine) { + wf_components->i2wavefronts = NULL; + wf_components->d2wavefronts = NULL; + } else { + wf_components->i2wavefronts = mm_allocator_calloc(mm_allocator,num_wavefronts,wavefront_t*,init_wf); + wf_components->d2wavefronts = mm_allocator_calloc(mm_allocator,num_wavefronts,wavefront_t*,init_wf); + } + } +} +void wavefront_components_allocate( + wavefront_components_t* const wf_components, + const int max_pattern_length, + const int max_text_length, + wavefront_penalties_t* const penalties, + const bool memory_modular, + const bool bt_piggyback, + mm_allocator_t* const mm_allocator) { + // Configuration + wf_components->memory_modular = memory_modular; + wf_components->bt_piggyback = bt_piggyback; + wf_components->mm_allocator = mm_allocator; // MM + // Allocate wavefronts + wavefront_components_dimensions( + wf_components,penalties, + max_pattern_length,max_text_length, + &wf_components->max_score_scope, + &wf_components->num_wavefronts); + wavefront_components_allocate_wf(wf_components, + max_pattern_length,max_text_length,penalties->distance_metric); + // Allocate victim wavefront (outside slab) + wavefront_t* const wavefront_victim = mm_allocator_alloc(mm_allocator,wavefront_t); + wavefront_allocate(wavefront_victim,WF_NULL_INIT_LENGTH,bt_piggyback,mm_allocator); + wavefront_init_victim(wavefront_victim,WF_NULL_INIT_LO,WF_NULL_INIT_HI); + wf_components->wavefront_victim = wavefront_victim; + // Allocate null wavefront (outside slab) + wavefront_t* const wavefront_null = mm_allocator_alloc(mm_allocator,wavefront_t); + wavefront_allocate(wavefront_null,WF_NULL_INIT_LENGTH,bt_piggyback,mm_allocator); + wavefront_init_null(wavefront_null,WF_NULL_INIT_LO,WF_NULL_INIT_HI); + wf_components->wavefront_null = wavefront_null; + // BT-Buffer + wf_components->bt_buffer = (bt_piggyback) ? wf_backtrace_buffer_new(mm_allocator) : NULL; +} +void wavefront_components_reap( + wavefront_components_t* const wf_components) { + // BT-Buffer + if (wf_components->bt_buffer) wf_backtrace_buffer_reap(wf_components->bt_buffer); +} +void wavefront_components_clear( + wavefront_components_t* const wf_components) { + // Wavefronts components + if (wf_components->memory_modular) { + const int num_wavefronts = wf_components->num_wavefronts; + const int wf_size = num_wavefronts*sizeof(wavefront_t*); + memset(wf_components->mwavefronts,0,wf_size); + if (wf_components->i1wavefronts) memset(wf_components->i1wavefronts,0,wf_size); + if (wf_components->d1wavefronts) memset(wf_components->d1wavefronts,0,wf_size); + if (wf_components->i2wavefronts) memset(wf_components->i2wavefronts,0,wf_size); + if (wf_components->d2wavefronts) memset(wf_components->d2wavefronts,0,wf_size); + } + wf_components->historic_max_hi = 0; + wf_components->historic_min_lo = 0; + // BT-Buffer + if (wf_components->bt_buffer) wf_backtrace_buffer_clear(wf_components->bt_buffer); +} +void wavefront_components_free_wf( + wavefront_components_t* const wf_components) { + // Parameters + mm_allocator_t* const mm_allocator = wf_components->mm_allocator; + // Wavefronts components + mm_allocator_free(mm_allocator,wf_components->mwavefronts); + if (wf_components->i1wavefronts) mm_allocator_free(mm_allocator,wf_components->i1wavefronts); + if (wf_components->d1wavefronts) mm_allocator_free(mm_allocator,wf_components->d1wavefronts); + if (wf_components->i2wavefronts) mm_allocator_free(mm_allocator,wf_components->i2wavefronts); + if (wf_components->d2wavefronts) mm_allocator_free(mm_allocator,wf_components->d2wavefronts); +} +void wavefront_components_free( + wavefront_components_t* const wf_components) { + // Parameters + mm_allocator_t* const mm_allocator = wf_components->mm_allocator; + // Wavefronts components + wavefront_components_free_wf(wf_components); + // Null wavefront + wavefront_free(wf_components->wavefront_null,mm_allocator); + mm_allocator_free(mm_allocator,wf_components->wavefront_null); + // Victim wavefront + wavefront_free(wf_components->wavefront_victim,mm_allocator); + mm_allocator_free(mm_allocator,wf_components->wavefront_victim); + // BT-Buffer + if (wf_components->bt_buffer) wf_backtrace_buffer_delete(wf_components->bt_buffer); +} +/* + * Resize + */ +void wavefront_components_resize( + wavefront_components_t* const wf_components, + const int max_pattern_length, + const int max_text_length, + wavefront_penalties_t* const penalties) { + // Compute dimensions + int num_wavefronts = 0; + wavefront_components_dimensions( + wf_components,penalties, + max_pattern_length,max_text_length, + &wf_components->max_score_scope,&num_wavefronts); + // Resize wavefronts components (if needed) + if (num_wavefronts > wf_components->num_wavefronts) { + wf_components->num_wavefronts = num_wavefronts; + wavefront_components_free_wf(wf_components); + wavefront_components_allocate_wf(wf_components, + max_pattern_length,max_text_length,penalties->distance_metric); + // BT-Buffer + if (wf_components->bt_buffer) wf_backtrace_buffer_clear(wf_components->bt_buffer); + } else { + wavefront_components_clear(wf_components); + } +} +void wavefront_components_resize_null__victim( + wavefront_components_t* const wf_components, + const int lo, + const int hi) { + // Resize null/victim wavefronts (if needed) + if (lo-1 < wf_components->wavefront_null->wf_elements_init_min || + hi+1 > wf_components->wavefront_null->wf_elements_init_max) { + // Parameters + mm_allocator_t* const mm_allocator = wf_components->mm_allocator; + // Expand and leave some leeway + const int wf_inc = (WAVEFRONT_LENGTH(lo,hi)*3)/2; + const int proposed_lo = lo - wf_inc/2; + const int proposed_hi = hi + wf_inc/2; + const int proposed_wavefront_length = WAVEFRONT_LENGTH(proposed_lo,proposed_hi); + // Reallocate victim wavefront + wavefront_resize(wf_components->wavefront_victim,proposed_wavefront_length,mm_allocator); + wavefront_init_victim(wf_components->wavefront_victim,proposed_lo,proposed_hi); + // Allocate null wavefront + wavefront_resize(wf_components->wavefront_null,proposed_wavefront_length,mm_allocator); + wavefront_init_null(wf_components->wavefront_null,proposed_lo,proposed_hi); + } +} +/* + * Mark wavefronts + */ +void wavefront_components_mark_backtrace( + wf_backtrace_buffer_t* const bt_buffer, + bitmap_t* const bitmap, + wavefront_t* const wavefront) { + // Parameters + wf_offset_t* const offsets = wavefront->offsets; + bt_block_idx_t* const bt_prev = wavefront->bt_prev; + const int lo = wavefront->lo; + const int hi = wavefront->hi; + // Mark all wavefront backtraces (batch mode) + wf_backtrace_buffer_mark_backtrace_batch( + bt_buffer,offsets+lo,bt_prev+lo,hi-lo+1,bitmap); + // int i; + // for (i=lo;i<=hi;++i) { + // if (offsets[i]>=0) wf_backtrace_buffer_mark_backtrace(bt_buffer,bt_prev[i],bitmap); + // } +} +void wavefront_components_mark_wavefronts( + wavefront_components_t* const wf_components, + bitmap_t* const bitmap, + const int score) { + // Parameters + wf_backtrace_buffer_t* const bt_buffer = wf_components->bt_buffer; + const int max_score_scope = wf_components->max_score_scope; + // Mark Active Working Set (AWS) + int i; + for (i=0;imax_score_scope; + // Mark M-wavefront + wavefront_t* const mwavefront = wf_components->mwavefronts[score_mod]; + if (mwavefront!=NULL) wavefront_components_mark_backtrace(bt_buffer,bitmap,mwavefront); + // Mark (I1/D1)-wavefronts + if (wf_components->i1wavefronts != NULL) { + wavefront_t* const i1wavefront = wf_components->i1wavefronts[score_mod]; + if (i1wavefront!=NULL) wavefront_components_mark_backtrace(bt_buffer,bitmap,i1wavefront); + wavefront_t* const d1wavefront = wf_components->d1wavefronts[score_mod]; + if (d1wavefront!=NULL) wavefront_components_mark_backtrace(bt_buffer,bitmap,d1wavefront); + // Mark (I2/D2)-wavefronts + if (wf_components->i2wavefronts != NULL) { + wavefront_t* const i2wavefront = wf_components->i2wavefronts[score_mod]; + if (i2wavefront!=NULL) wavefront_components_mark_backtrace(bt_buffer,bitmap,i2wavefront); + wavefront_t* const d2wavefront = wf_components->d2wavefronts[score_mod]; + if (d2wavefront!=NULL) wavefront_components_mark_backtrace(bt_buffer,bitmap,d2wavefront); + } + } + } + // Update counters in marked bitmap + bitmap_update_counters(bitmap); +} +/* + * Translate block-idxs + */ +void wavefront_components_translate_idx( + wavefront_components_t* const wf_components, + bitmap_t* const bitmap, + wavefront_t* const wavefront) { + // Parameters + wf_offset_t* const offsets = wavefront->offsets; + bt_block_idx_t* const bt_prev = wavefront->bt_prev; + const int lo = wavefront->lo; + const int hi = wavefront->hi; + const bt_block_idx_t num_compacted_blocks = wf_components->bt_buffer->num_compacted_blocks; + // Translate all wavefront block-idxs + int k; + for (k=lo;k<=hi;++k) { + if (offsets[k]>=0) { // NOTE bt_prev[k] >= num_compacted_blocks + bt_prev[k] = (bt_prev[k]==BT_BLOCK_IDX_NULL) ? + BT_BLOCK_IDX_NULL : + num_compacted_blocks + bitmap_erank(bitmap,bt_prev[k]); + } + } +} +void wavefront_components_translate_wavefronts( + wavefront_components_t* const wf_components, + bitmap_t* const bitmap, + const int score) { + // Mark Active Working Set (AWS) + const int max_score_scope = wf_components->max_score_scope; + int i; + for (i=0;imax_score_scope; + // Mark M-wavefront + wavefront_t* const mwavefront = wf_components->mwavefronts[score_mod]; + if (mwavefront!=NULL) wavefront_components_translate_idx(wf_components,bitmap,mwavefront); + // Mark (I1/D1)-wavefronts + if (wf_components->i1wavefronts != NULL) { + wavefront_t* const i1wavefront = wf_components->i1wavefronts[score_mod]; + if (i1wavefront!=NULL) wavefront_components_translate_idx(wf_components,bitmap,i1wavefront); + wavefront_t* const d1wavefront = wf_components->d1wavefronts[score_mod]; + if (d1wavefront!=NULL) wavefront_components_translate_idx(wf_components,bitmap,d1wavefront); + // Mark (I2/D2)-wavefronts + if (wf_components->i2wavefronts != NULL) { + wavefront_t* const i2wavefront = wf_components->i2wavefronts[score_mod]; + if (i2wavefront!=NULL) wavefront_components_translate_idx(wf_components,bitmap,i2wavefront); + wavefront_t* const d2wavefront = wf_components->d2wavefronts[score_mod]; + if (d2wavefront!=NULL) wavefront_components_translate_idx(wf_components,bitmap,d2wavefront); + } + } + } +} +/* + * Compact + */ +void wavefront_components_compact_bt_buffer( + wavefront_components_t* const wf_components, + const int score, + const int verbose) { + // PROFILE + profiler_timer_t timer; + if (verbose >= 3) { timer_reset(&timer); timer_start(&timer); } + // Parameters + wf_backtrace_buffer_t* const bt_buffer = wf_components->bt_buffer; + const uint64_t bt_buffer_used = wf_backtrace_buffer_get_used(bt_buffer); + // Allocate bitmap + bitmap_t* const bitmap = bitmap_new(bt_buffer_used,wf_components->mm_allocator); + // Mark Active Working Set (AWS) + wavefront_components_mark_wavefronts(wf_components,bitmap,score); + // Compact marked blocks (also translates idxs to compacted positions) + const bt_block_idx_t total_compacted_blocks = + wf_backtrace_buffer_compact_marked(bt_buffer,bitmap,verbose); + // Translate Active Working Set (AWS) + wavefront_components_translate_wavefronts(wf_components,bitmap,score); + // Set new compacted blocks + wf_backtrace_buffer_set_num_compacted_blocks(bt_buffer,total_compacted_blocks); + // Free + bitmap_delete(bitmap); + // PROFILE + if (verbose >= 3) { + timer_stop(&timer); + fprintf(stderr,"["); + timer_print_total(stderr,&timer); + fprintf(stderr,"]\n"); + } +} + diff --git a/src/lib/wfa2/wavefront/wavefront_components.h b/src/lib/wfa2/wavefront/wavefront_components.h new file mode 100644 index 000000000..58728666a --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_components.h @@ -0,0 +1,105 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront components module + */ + +#ifndef WAVEFRONT_WAVEFRONT_COMPONENTS_H_ +#define WAVEFRONT_WAVEFRONT_COMPONENTS_H_ + +#include "../utils/commons.h" +#include "../wavefront/wavefront.h" +#include "../wavefront/wavefront_backtrace_buffer.h" +#include "../wavefront/wavefront_penalties.h" + +/* + * Wavefront Components + */ +typedef struct { + // Configuration + bool memory_modular; // Memory strategy (modular wavefronts) + bool bt_piggyback; // Backtrace Piggyback + // Wavefronts dimensions + int num_wavefronts; // Total number of allocated wavefronts + int max_score_scope; // Maximum score-difference between dependent wavefronts + int historic_max_hi; // Maximum WF hi-limit seen during current alignment + int historic_min_lo; // Minimum WF lo-limit seen during current alignment + // Wavefronts + wavefront_t** mwavefronts; // M-wavefronts + wavefront_t** i1wavefronts; // I1-wavefronts + wavefront_t** i2wavefronts; // I2-wavefronts + wavefront_t** d1wavefronts; // D1-wavefronts + wavefront_t** d2wavefronts; // D2-wavefronts + wavefront_t* wavefront_null; // Null wavefront (orthogonal reading) + wavefront_t* wavefront_victim; // Dummy wavefront (orthogonal writing) + // BT-Buffer + wf_backtrace_buffer_t* bt_buffer; // Backtrace Buffer + // MM + mm_allocator_t* mm_allocator; // MM-Allocator +} wavefront_components_t; + +/* + * Setup + */ +void wavefront_components_allocate( + wavefront_components_t* const wf_components, + const int max_pattern_length, + const int max_text_length, + wavefront_penalties_t* const penalties, + const bool memory_modular, + const bool bt_piggyback, + mm_allocator_t* const mm_allocator); +void wavefront_components_reap( + wavefront_components_t* const wf_components); +void wavefront_components_clear( + wavefront_components_t* const wf_components); +void wavefront_components_free( + wavefront_components_t* const wf_components); + +/* + * Resize + */ +void wavefront_components_resize( + wavefront_components_t* const wf_components, + const int max_pattern_length, + const int max_text_length, + wavefront_penalties_t* const penalties); +void wavefront_components_resize_null__victim( + wavefront_components_t* const wf_components, + const int lo, + const int hi); + +/* + * Compact + */ +void wavefront_components_compact_bt_buffer( + wavefront_components_t* const wf_components, + const int score, + const int verbose); + +#endif /* WAVEFRONT_WAVEFRONT_COMPONENTS_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_compute.c b/src/lib/wfa2/wavefront/wavefront_compute.c new file mode 100644 index 000000000..b17a19ac3 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_compute.c @@ -0,0 +1,652 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for computing wavefronts + */ + +#include "../utils/string_padded.h" +#include "../alignment/affine2p_penalties.h" +#include "wavefront_compute.h" + +/* + * Compute limits + */ +void wavefront_compute_limits_input( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + int* const lo, + int* const hi) { + // Parameters + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + const wavefront_t* const m_misms = wavefront_set->in_mwavefront_misms; + const wavefront_t* const m_open1 = wavefront_set->in_mwavefront_open1; + // Init + int min_lo = m_misms->lo; + int max_hi = m_misms->hi; + // Gap-linear + if (min_lo > m_open1->lo-1) min_lo = m_open1->lo-1; + if (max_hi < m_open1->hi+1) max_hi = m_open1->hi+1; + if (distance_metric == gap_linear) { + *lo = min_lo; + *hi = max_hi; + return; + } + // Parameters + const wavefront_t* const i1_ext = wavefront_set->in_i1wavefront_ext; + const wavefront_t* const d1_ext = wavefront_set->in_d1wavefront_ext; + // Gap-affine + if (min_lo > i1_ext->lo+1) min_lo = i1_ext->lo+1; + if (max_hi < i1_ext->hi+1) max_hi = i1_ext->hi+1; + if (min_lo > d1_ext->lo-1) min_lo = d1_ext->lo-1; + if (max_hi < d1_ext->hi-1) max_hi = d1_ext->hi-1; + if (distance_metric == gap_affine) { + *lo = min_lo; + *hi = max_hi; + return; + } + // Parameters + const wavefront_t* const m_open2 = wavefront_set->in_mwavefront_open2; + const wavefront_t* const i2_ext = wavefront_set->in_i2wavefront_ext; + const wavefront_t* const d2_ext = wavefront_set->in_d2wavefront_ext; + // Gap-affine-2p + if (min_lo > m_open2->lo-1) min_lo = m_open2->lo-1; + if (max_hi < m_open2->hi+1) max_hi = m_open2->hi+1; + if (min_lo > i2_ext->lo+1) min_lo = i2_ext->lo+1; + if (max_hi < i2_ext->hi+1) max_hi = i2_ext->hi+1; + if (min_lo > d2_ext->lo-1) min_lo = d2_ext->lo-1; + if (max_hi < d2_ext->hi-1) max_hi = d2_ext->hi-1; + *lo = min_lo; + *hi = max_hi; +} +void wavefront_compute_limits_output( + wavefront_aligner_t* const wf_aligner, + const int lo, + const int hi, + int* const effective_lo, + int* const effective_hi) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const int max_score_scope = wf_components->max_score_scope; + // Add padding to avoid compute-kernel peeling + const int eff_lo = lo - (max_score_scope + 1); + const int eff_hi = hi + (max_score_scope + 1); + // Consider historic (to avoid errors using heuristics) + *effective_lo = MIN(eff_lo,wf_components->historic_min_lo); + *effective_hi = MAX(eff_hi,wf_components->historic_max_hi); + wf_components->historic_min_lo = *effective_lo; + wf_components->historic_max_hi = *effective_hi; +} +/* + * Score translation + */ +int wavefront_compute_classic_score( + wavefront_aligner_t* const wf_aligner, + const int pattern_length, + const int text_length, + const int wf_score) { + // Parameters + const int swg_match = -(wf_aligner->penalties.match); + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + // Adapt score + if (distance_metric <= edit) return wf_score; + if (swg_match == 0) return -wf_score; + return WF_SCORE_TO_SW_SCORE(swg_match,pattern_length,text_length,wf_score); +} +/* + * Compute ends-free init conditions + */ +bool wavefront_compute_endsfree_required( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Parameters + alignment_form_t* const alg_form = &wf_aligner->alignment_form; + wavefront_penalties_t* const penalties = &wf_aligner->penalties; + // Return is ends-free initialization is required + if (penalties->match == 0) return false; + if (alg_form->span != alignment_endsfree) return false; + if (score % (-penalties->match) != 0) return false; + // Ok + return true; +} +void wavefront_compute_endsfree_limits( + wavefront_aligner_t* const wf_aligner, + const int score, + int* const lo, + int* const hi) { + // Parameters + alignment_form_t* const alg_form = &wf_aligner->alignment_form; + wavefront_penalties_t* const penalties = &wf_aligner->penalties; + // Consider ends-free conditions + const int endsfree_k = score/(-penalties->match); + *hi = (alg_form->text_begin_free >= endsfree_k) ? endsfree_k : INT_MIN; + *lo = (alg_form->pattern_begin_free >= endsfree_k) ? -endsfree_k : INT_MAX; +} +void wavefront_compute_endsfree_init_offset( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront, + const int k, + const int v, + const int h) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + wf_offset_t* const offsets = wavefront->offsets; + // Set offset + offsets[k] = DPMATRIX_OFFSET(h,v); + if (wf_components->bt_piggyback) { + wavefront->bt_pcigar[k] = 0; + wavefront->bt_prev[k] = + wf_backtrace_buffer_init_block(wf_components->bt_buffer,v,h); + } +} +void wavefront_compute_endsfree_init( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront, + const int score) { + // Parameters + alignment_form_t* const alg_form = &wf_aligner->alignment_form; + wavefront_penalties_t* const penalties = &wf_aligner->penalties; + const int lo = wavefront->lo; + const int hi = wavefront->hi; + // Consider ends-free conditions + int endsfree_k = score/(-penalties->match); + wf_offset_t* const offsets = wavefront->offsets; + // Consider text begin-free + int k; + if (alg_form->text_begin_free >= endsfree_k) { + if (hi >= endsfree_k) { + if (offsets[endsfree_k] <= DPMATRIX_OFFSET(endsfree_k,0)) { + wavefront_compute_endsfree_init_offset(wf_aligner,wavefront,endsfree_k,0,endsfree_k); + } + } else { + for (k=hi+1;khi = endsfree_k; + } + } + // Consider pattern begin-free + if (alg_form->pattern_begin_free >= endsfree_k) { + endsfree_k = -endsfree_k; + if (lo <= endsfree_k) { + if (offsets[endsfree_k] <= DPMATRIX_OFFSET(0,endsfree_k)) { + wavefront_compute_endsfree_init_offset(wf_aligner,wavefront,endsfree_k,-endsfree_k,0); + } + } else { + wavefront_compute_endsfree_init_offset(wf_aligner,wavefront,endsfree_k,-endsfree_k,0); + for (k=endsfree_k+1;klo = endsfree_k; + } + } +} +wavefront_t* wavefront_compute_endsfree_allocate_null( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Parameters + wavefront_slab_t* const wavefront_slab = wf_aligner->wavefront_slab; + alignment_form_t* const alg_form = &wf_aligner->alignment_form; + wavefront_penalties_t* const penalties = &wf_aligner->penalties; + // Consider ends-free conditions + const int endsfree_k = score/(-penalties->match); + const bool text_begin_free = (alg_form->text_begin_free >= endsfree_k); + const bool pattern_begin_free = (alg_form->pattern_begin_free >= endsfree_k); + int lo = 0, hi = 0; + if (text_begin_free && pattern_begin_free) { + lo = -endsfree_k; + hi = endsfree_k; + } else if (text_begin_free) { + lo = endsfree_k; + hi = endsfree_k; + } else if (pattern_begin_free) { + lo = -endsfree_k; + hi = -endsfree_k; + } + // Compute effective hi/lo dimensions + int effective_lo, effective_hi; + wavefront_compute_limits_output(wf_aligner,lo,hi,&effective_lo,&effective_hi); + // Allocate & initialize + wavefront_t* const wavefront = wavefront_slab_allocate(wavefront_slab,effective_lo,effective_hi); + wf_offset_t* const offsets = wavefront->offsets; + int k; + for (k=lo+1;klo = lo; + wavefront->hi = hi; + // Return + return wavefront; +} +/* + * Input wavefronts (fetch) + */ +wavefront_t* wavefront_compute_get_mwavefront( + wavefront_components_t* const wf_components, + const int score_mod) { + return (score_mod < 0 || + wf_components->mwavefronts[score_mod] == NULL || + wf_components->mwavefronts[score_mod]->null) ? + wf_components->wavefront_null : wf_components->mwavefronts[score_mod]; +} +wavefront_t* wavefront_compute_get_i1wavefront( + wavefront_components_t* const wf_components, + const int score_mod) { + return (score_mod < 0 || + wf_components->i1wavefronts[score_mod] == NULL || + wf_components->i1wavefronts[score_mod]->null) ? + wf_components->wavefront_null : wf_components->i1wavefronts[score_mod]; +} +wavefront_t* wavefront_compute_get_i2wavefront( + wavefront_components_t* const wf_components, + const int score_mod) { + return (score_mod < 0 || + wf_components->i2wavefronts[score_mod] == NULL || + wf_components->i2wavefronts[score_mod]->null) ? + wf_components->wavefront_null : wf_components->i2wavefronts[score_mod]; +} +wavefront_t* wavefront_compute_get_d1wavefront( + wavefront_components_t* const wf_components, + const int score_mod) { + return (score_mod < 0 || + wf_components->d1wavefronts[score_mod] == NULL || + wf_components->d1wavefronts[score_mod]->null) ? + wf_components->wavefront_null : wf_components->d1wavefronts[score_mod]; +} +wavefront_t* wavefront_compute_get_d2wavefront( + wavefront_components_t* const wf_components, + const int score_mod) { + return (score_mod < 0 || + wf_components->d2wavefronts[score_mod] == NULL || + wf_components->d2wavefronts[score_mod]->null) ? + wf_components->wavefront_null : wf_components->d2wavefronts[score_mod]; +} +void wavefront_compute_fetch_input( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int score) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + // Compute scores + const wavefront_penalties_t* const penalties = &(wf_aligner->penalties); + if (distance_metric == gap_linear) { + int mismatch = score - penalties->mismatch; + int gap_open1 = score - penalties->gap_opening1; + // Modular wavefront + if (wf_components->memory_modular) { + const int max_score_scope = wf_components->max_score_scope; + if (mismatch > 0) mismatch = mismatch % max_score_scope; + if (gap_open1 > 0) gap_open1 = gap_open1 % max_score_scope; + } + // Fetch wavefronts + wavefront_set->in_mwavefront_misms = wavefront_compute_get_mwavefront(wf_components,mismatch); + wavefront_set->in_mwavefront_open1 = wavefront_compute_get_mwavefront(wf_components,gap_open1); + } else { // distance_metric == gap_affine || distance_metric == gap_affine_2p + int mismatch = score - penalties->mismatch; + int gap_open1 = score - penalties->gap_opening1 - penalties->gap_extension1; + int gap_extend1 = score - penalties->gap_extension1; + int gap_open2 = score - penalties->gap_opening2 - penalties->gap_extension2; + int gap_extend2 = score - penalties->gap_extension2; + // Modular wavefront + if (wf_components->memory_modular) { + const int max_score_scope = wf_components->max_score_scope; + if (mismatch > 0) mismatch = mismatch % max_score_scope; + if (gap_open1 > 0) gap_open1 = gap_open1 % max_score_scope; + if (gap_extend1 > 0) gap_extend1 = gap_extend1 % max_score_scope; + if (gap_open2 > 0) gap_open2 = gap_open2 % max_score_scope; + if (gap_extend2 > 0) gap_extend2 = gap_extend2 % max_score_scope; + } + // Fetch wavefronts + wavefront_set->in_mwavefront_misms = wavefront_compute_get_mwavefront(wf_components,mismatch); + wavefront_set->in_mwavefront_open1 = wavefront_compute_get_mwavefront(wf_components,gap_open1); + wavefront_set->in_i1wavefront_ext = wavefront_compute_get_i1wavefront(wf_components,gap_extend1); + wavefront_set->in_d1wavefront_ext = wavefront_compute_get_d1wavefront(wf_components,gap_extend1); + if (distance_metric == gap_affine) return; + wavefront_set->in_mwavefront_open2 = wavefront_compute_get_mwavefront(wf_components,gap_open2); + wavefront_set->in_i2wavefront_ext = wavefront_compute_get_i2wavefront(wf_components,gap_extend2); + wavefront_set->in_d2wavefront_ext = wavefront_compute_get_d2wavefront(wf_components,gap_extend2); + } +} +/* + * Output wavefronts (allocate) + */ +void wavefront_compute_free_output( + wavefront_aligner_t* const wf_aligner, + const int score_mod) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + wavefront_slab_t* const wavefront_slab = wf_aligner->wavefront_slab; + // Free + if (wf_components->mwavefronts[score_mod]) { + wavefront_slab_free(wavefront_slab,wf_components->mwavefronts[score_mod]); + } + if (distance_metric == gap_linear) return; + if (wf_components->i1wavefronts[score_mod]) { + wavefront_slab_free(wavefront_slab,wf_components->i1wavefronts[score_mod]); + } + if (wf_components->d1wavefronts[score_mod]) { + wavefront_slab_free(wavefront_slab,wf_components->d1wavefronts[score_mod]); + } + if (distance_metric == gap_affine) return; + if (wf_components->i2wavefronts[score_mod]) { + wavefront_slab_free(wavefront_slab,wf_components->i2wavefronts[score_mod]); + } + if (wf_components->d2wavefronts[score_mod]) { + wavefront_slab_free(wavefront_slab,wf_components->d2wavefronts[score_mod]); + } +} +void wavefront_compute_allocate_output_null( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Parameters + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + // Modular wavefront + int score_mod = score; + if (wf_components->memory_modular) { + score_mod = score % wf_components->max_score_scope; + wavefront_compute_free_output(wf_aligner,score_mod); + } + // Consider ends-free (M!=0) + if (wavefront_compute_endsfree_required(wf_aligner,score)) { + wf_components->mwavefronts[score_mod] = + wavefront_compute_endsfree_allocate_null(wf_aligner,score); + } else { + wf_components->mwavefronts[score_mod] = NULL; + } + // Nullify Wavefronts + if (distance_metric == gap_linear) return; + wf_components->i1wavefronts[score_mod] = NULL; + wf_components->d1wavefronts[score_mod] = NULL; + if (distance_metric == gap_affine) return; + wf_components->i2wavefronts[score_mod] = NULL; + wf_components->d2wavefronts[score_mod] = NULL; +} +void wavefront_compute_allocate_output( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int score, + const int lo, + const int hi) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + wavefront_slab_t* const wavefront_slab = wf_aligner->wavefront_slab; + // Consider ends-free (M!=0) + int effective_lo, effective_hi; + if (wavefront_compute_endsfree_required(wf_aligner,score)) { + int endsfree_lo, endsfree_hi; + wavefront_compute_endsfree_limits(wf_aligner,score,&endsfree_lo,&endsfree_hi); + effective_lo = MIN(lo,endsfree_lo); + effective_hi = MAX(hi,endsfree_hi); + } else { + effective_lo = lo; + effective_hi = hi; + } + // Compute effective hi/lo dimensions + wavefront_compute_limits_output( + wf_aligner,effective_lo,effective_hi, + &effective_lo,&effective_hi); + // Resize null/victim wavefronts + wavefront_components_resize_null__victim(wf_components,effective_lo,effective_hi); + // Modular wavefront + int score_mod = score; + if (wf_components->memory_modular) { + score_mod = score % wf_components->max_score_scope; + wavefront_compute_free_output(wf_aligner,score_mod); + } + // Check + if (score_mod >= wf_components->num_wavefronts) { + fprintf(stderr,"[WFA::Compute] Maximum allocated wavefronts reached\n"); + exit(1); + } + // Allocate M-Wavefront + wavefront_set->out_mwavefront = wavefront_slab_allocate(wavefront_slab,effective_lo,effective_hi); + wf_components->mwavefronts[score_mod] = wavefront_set->out_mwavefront; + wf_components->mwavefronts[score_mod]->lo = lo; + wf_components->mwavefronts[score_mod]->hi = hi; + if (distance_metric == gap_linear) return; + // Allocate I1-Wavefront + if (!wavefront_set->in_mwavefront_open1->null || !wavefront_set->in_i1wavefront_ext->null) { + wavefront_set->out_i1wavefront = wavefront_slab_allocate(wavefront_slab,effective_lo,effective_hi); + wf_components->i1wavefronts[score_mod] = wavefront_set->out_i1wavefront; + wf_components->i1wavefronts[score_mod]->lo = lo; + wf_components->i1wavefronts[score_mod]->hi = hi; + } else { + wavefront_set->out_i1wavefront = wf_components->wavefront_victim; + wf_components->i1wavefronts[score_mod] = NULL; + } + // Allocate D1-Wavefront + if (!wavefront_set->in_mwavefront_open1->null || !wavefront_set->in_d1wavefront_ext->null) { + wavefront_set->out_d1wavefront = wavefront_slab_allocate(wavefront_slab,effective_lo,effective_hi); + wf_components->d1wavefronts[score_mod] = wavefront_set->out_d1wavefront; + wf_components->d1wavefronts[score_mod]->lo = lo; + wf_components->d1wavefronts[score_mod]->hi = hi; + } else { + wavefront_set->out_d1wavefront = wf_components->wavefront_victim; + wf_components->d1wavefronts[score_mod] = NULL; + } + if (distance_metric == gap_affine) return; + // Allocate I2-Wavefront + if (!wavefront_set->in_mwavefront_open2->null || !wavefront_set->in_i2wavefront_ext->null) { + wavefront_set->out_i2wavefront = wavefront_slab_allocate(wavefront_slab,effective_lo,effective_hi); + wf_components->i2wavefronts[score_mod] = wavefront_set->out_i2wavefront; + wf_components->i2wavefronts[score_mod]->lo = lo; + wf_components->i2wavefronts[score_mod]->hi = hi; + } else { + wavefront_set->out_i2wavefront = wf_components->wavefront_victim; + wf_components->i2wavefronts[score_mod] = NULL; + } + // Allocate D2-Wavefront + if (!wavefront_set->in_mwavefront_open2->null || !wavefront_set->in_d2wavefront_ext->null) { + wavefront_set->out_d2wavefront = wavefront_slab_allocate(wavefront_slab,effective_lo,effective_hi); + wf_components->d2wavefronts[score_mod] = wavefront_set->out_d2wavefront; + wf_components->d2wavefronts[score_mod]->lo = lo; + wf_components->d2wavefronts[score_mod]->hi = hi; + } else { + wavefront_set->out_d2wavefront = wf_components->wavefront_victim; + wf_components->d2wavefronts[score_mod] = NULL; + } +} +/* + * Initialize wavefronts ends + */ +void wavefront_compute_init_ends_wf_lower( + wavefront_t* const wavefront, + const int min_lo) { + // Check initialization (lowest element) + if (wavefront->wf_elements_init_min <= min_lo) return; + // Initialize lower elements + wf_offset_t* const offsets = wavefront->offsets; + const int min_init = MIN(wavefront->wf_elements_init_min,wavefront->lo); + int k; + for (k=min_lo;kwf_elements_init_min = min_lo; +} +void wavefront_compute_init_ends_wf_higher( + wavefront_t* const wavefront, + const int max_hi) { + // Check initialization (highest element) + if (wavefront->wf_elements_init_max >= max_hi) return; + // Initialize lower elements + wf_offset_t* const offsets = wavefront->offsets; + const int max_init = MAX(wavefront->wf_elements_init_max,wavefront->hi); + int k; + for (k=max_init+1;k<=max_hi;++k) { + offsets[k] = WAVEFRONT_OFFSET_NULL; + } + // Set new maximum + wavefront->wf_elements_init_max = max_hi; +} +void wavefront_compute_init_ends( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Parameters + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + // Init missing elements, instead of loop peeling (M) + const bool m_misms_null = wavefront_set->in_mwavefront_misms->null; + const bool m_gap1_null = wavefront_set->in_mwavefront_open1->null; + if (!m_misms_null) { + wavefront_compute_init_ends_wf_higher(wavefront_set->in_mwavefront_misms,hi); + wavefront_compute_init_ends_wf_lower(wavefront_set->in_mwavefront_misms,lo); + } + if (!m_gap1_null) { + wavefront_compute_init_ends_wf_higher(wavefront_set->in_mwavefront_open1,hi+1); + wavefront_compute_init_ends_wf_lower(wavefront_set->in_mwavefront_open1,lo-1); + } + if (distance_metric == gap_linear) return; + // Init missing elements, instead of loop peeling (Open1/I1/D1) + const bool i1_ext_null = wavefront_set->in_i1wavefront_ext->null; + const bool d1_ext_null = wavefront_set->in_d1wavefront_ext->null; + if (!i1_ext_null) { + wavefront_compute_init_ends_wf_higher(wavefront_set->in_i1wavefront_ext,hi); + wavefront_compute_init_ends_wf_lower(wavefront_set->in_i1wavefront_ext,lo-1); + } + if (!d1_ext_null) { + wavefront_compute_init_ends_wf_higher(wavefront_set->in_d1wavefront_ext,hi+1); + wavefront_compute_init_ends_wf_lower(wavefront_set->in_d1wavefront_ext,lo); + } + if (distance_metric == gap_affine) return; + // Init missing elements, instead of loop peeling (Open2/I2/D2) + const bool m_gap2_null = wavefront_set->in_mwavefront_open2->null; + const bool i2_ext_null = wavefront_set->in_i2wavefront_ext->null; + const bool d2_ext_null = wavefront_set->in_d2wavefront_ext->null; + if (!m_gap2_null) { + wavefront_compute_init_ends_wf_higher(wavefront_set->in_mwavefront_open2,hi+1); + wavefront_compute_init_ends_wf_lower(wavefront_set->in_mwavefront_open2,lo-1); + } + if (!i2_ext_null) { + wavefront_compute_init_ends_wf_higher(wavefront_set->in_i2wavefront_ext,hi); + wavefront_compute_init_ends_wf_lower(wavefront_set->in_i2wavefront_ext,lo-1); + } + if (!d2_ext_null) { + wavefront_compute_init_ends_wf_higher(wavefront_set->in_d2wavefront_ext,hi+1); + wavefront_compute_init_ends_wf_lower(wavefront_set->in_d2wavefront_ext,lo); + } +} +/* + * Trim wavefronts ends + */ +void wavefront_compute_trim_ends( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + wf_offset_t* const offsets = wavefront->offsets; + // Trim from hi + int k; + const int lo = wavefront->lo; + for (k=wavefront->hi;k>=lo;--k) { + // Fetch offset + const wf_offset_t offset = offsets[k]; + // Check boundaries + const uint32_t h = WAVEFRONT_H(k,offset); // Make unsigned to avoid checking negative + const uint32_t v = WAVEFRONT_V(k,offset); // Make unsigned to avoid checking negative + if (h <= text_length && v <= pattern_length) break; + } + wavefront->hi = k; // Set new hi + wavefront->wf_elements_init_max = k; + // Trim from lo + const int hi = wavefront->hi; + for (k=wavefront->lo;k<=hi;++k) { + // Fetch offset + const wf_offset_t offset = offsets[k]; + // Check boundaries + const uint32_t h = WAVEFRONT_H(k,offset); // Make unsigned to avoid checking negative + const uint32_t v = WAVEFRONT_V(k,offset); // Make unsigned to avoid checking negative + if (h <= text_length && v <= pattern_length) break; + } + wavefront->lo = k; // Set new lo + wavefront->wf_elements_init_min = k; + wavefront->null = (wavefront->lo > wavefront->hi); +} +void wavefront_compute_process_ends( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int score) { + // Parameters + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + // Consider ends-free (M!=0) + if (wavefront_compute_endsfree_required(wf_aligner,score)) { + wavefront_compute_endsfree_init(wf_aligner,wavefront_set->out_mwavefront,score); + } + // Trim ends from non-null WFs + if (wavefront_set->out_mwavefront) wavefront_compute_trim_ends(wf_aligner,wavefront_set->out_mwavefront); + if (distance_metric == gap_linear) return; + if (wavefront_set->out_i1wavefront) wavefront_compute_trim_ends(wf_aligner,wavefront_set->out_i1wavefront); + if (wavefront_set->out_d1wavefront) wavefront_compute_trim_ends(wf_aligner,wavefront_set->out_d1wavefront); + if (distance_metric == gap_affine) return; + if (wavefront_set->out_i2wavefront) wavefront_compute_trim_ends(wf_aligner,wavefront_set->out_i2wavefront); + if (wavefront_set->out_d2wavefront) wavefront_compute_trim_ends(wf_aligner,wavefront_set->out_d2wavefront); +} +/* + * Multithread dispatcher + */ +#ifdef WFA_PARALLEL +int wavefront_compute_num_threads( + wavefront_aligner_t* const wf_aligner, + const int lo, + const int hi) { + // Parameters + const int max_num_threads = wf_aligner->system.max_num_threads; + if (max_num_threads == 1) return 1; + const int min_offsets_per_thread = wf_aligner->system.min_offsets_per_thread; + // Compute minimum work-chunks worth spawning threads + const int num_chunks = WAVEFRONT_LENGTH(lo,hi)/min_offsets_per_thread; + const int max_workers = MIN(num_chunks,max_num_threads); + return MAX(max_workers,1); +} +void wavefront_compute_thread_limits( + const int thread_id, + const int num_theads, + const int lo, + const int hi, + int* const thread_lo, + int* const thread_hi) { + const int chunk_size = WAVEFRONT_LENGTH(lo,hi)/num_theads; + const int t_lo = lo + thread_id*chunk_size; + const int t_hi = (thread_id+1 == num_theads) ? hi : t_lo + chunk_size - 1; + *thread_lo = t_lo; + *thread_hi = t_hi; +} +#endif + diff --git a/src/lib/wfa2/wavefront/wavefront_compute.h b/src/lib/wfa2/wavefront/wavefront_compute.h new file mode 100644 index 000000000..add81a697 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_compute.h @@ -0,0 +1,121 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for computing wavefronts + */ + +#ifndef WAVEFRONT_COMPUTE_H_ +#define WAVEFRONT_COMPUTE_H_ + +#include "wavefront_aligner.h" + +/* + * Compute limits + */ +void wavefront_compute_limits_input( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + int* const lo, + int* const hi); +void wavefront_compute_limits_output( + wavefront_aligner_t* const wf_aligner, + const int lo, + const int hi, + int* const effective_lo, + int* const effective_hi); + +/* + * Score translation + */ +int wavefront_compute_classic_score( + wavefront_aligner_t* const wf_aligner, + const int pattern_length, + const int text_length, + const int wf_score); + +/* + * Input wavefronts (fetch) + */ +void wavefront_compute_fetch_input( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int score); + +/* + * Output wavefronts (allocate) + */ +void wavefront_compute_allocate_output_null( + wavefront_aligner_t* const wf_aligner, + const int score); +void wavefront_compute_allocate_output( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int score, + const int lo, + const int hi); + +/* + * Initialize wavefronts ends + */ +void wavefront_compute_init_ends( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int lo, + const int hi); + +/* + * Process wavefronts ends + */ +void wavefront_compute_trim_ends( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront); +void wavefront_compute_process_ends( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int score); + +/* + * Multithread dispatcher + */ +#ifdef WFA_PARALLEL +int wavefront_compute_num_threads( + wavefront_aligner_t* const wf_aligner, + const int lo, + const int hi); +void wavefront_compute_thread_limits( + const int thread_id, + const int num_theads, + const int lo, + const int hi, + int* const thread_lo, + int* const thread_hi); +#else +#define wavefront_compute_num_threads(wf_aligner,lo,hi) 1 +#endif + +#endif /* WAVEFRONT_COMPUTE_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_compute_affine.c b/src/lib/wfa2/wavefront/wavefront_compute_affine.c new file mode 100644 index 000000000..484c99b04 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_compute_affine.c @@ -0,0 +1,259 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for computing wavefronts (gap-affine) + */ + +#include "../utils/string_padded.h" +#include "wavefront_compute.h" +#include "wavefront_backtrace_offload.h" + +#ifdef WFA_PARALLEL +#include +#endif + +/* + * Compute Kernels + */ +void wavefront_compute_affine_idm( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + // In Offsets + const wf_offset_t* const m_misms = wavefront_set->in_mwavefront_misms->offsets; + const wf_offset_t* const m_open1 = wavefront_set->in_mwavefront_open1->offsets; + const wf_offset_t* const i1_ext = wavefront_set->in_i1wavefront_ext->offsets; + const wf_offset_t* const d1_ext = wavefront_set->in_d1wavefront_ext->offsets; + // Out Offsets + wf_offset_t* const out_m = wavefront_set->out_mwavefront->offsets; + wf_offset_t* const out_i1 = wavefront_set->out_i1wavefront->offsets; + wf_offset_t* const out_d1 = wavefront_set->out_d1wavefront->offsets; + // Compute-Next kernel loop + int k; + PRAGMA_LOOP_VECTORIZE + for (k=lo;k<=hi;++k) { + // Update I1 + const wf_offset_t ins1_o = m_open1[k-1]; + const wf_offset_t ins1_e = i1_ext[k-1]; + const wf_offset_t ins1 = MAX(ins1_o,ins1_e) + 1; + out_i1[k] = ins1; + // Update D1 + const wf_offset_t del1_o = m_open1[k+1]; + const wf_offset_t del1_e = d1_ext[k+1]; + const wf_offset_t del1 = MAX(del1_o,del1_e); + out_d1[k] = del1; + // Update M + const wf_offset_t misms = m_misms[k] + 1; + wf_offset_t max = MAX(del1,MAX(misms,ins1)); + // Adjust offset out of boundaries !(h>tlen,v>plen) (here to allow vectorization) + const wf_unsigned_offset_t h = WAVEFRONT_H(k,max); // Make unsigned to avoid checking negative + const wf_unsigned_offset_t v = WAVEFRONT_V(k,max); // Make unsigned to avoid checking negative + if (h > text_length) max = WAVEFRONT_OFFSET_NULL; + if (v > pattern_length) max = WAVEFRONT_OFFSET_NULL; + out_m[k] = max; + } +} +/* + * Compute Kernel (Piggyback) + */ +void wavefront_compute_affine_idm_piggyback( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + // In Offsets + const wf_offset_t* const m_misms = wavefront_set->in_mwavefront_misms->offsets; + const wf_offset_t* const m_open1 = wavefront_set->in_mwavefront_open1->offsets; + const wf_offset_t* const i1_ext = wavefront_set->in_i1wavefront_ext->offsets; + const wf_offset_t* const d1_ext = wavefront_set->in_d1wavefront_ext->offsets; + // Out Offsets + wf_offset_t* const out_m = wavefront_set->out_mwavefront->offsets; + wf_offset_t* const out_i1 = wavefront_set->out_i1wavefront->offsets; + wf_offset_t* const out_d1 = wavefront_set->out_d1wavefront->offsets; + // In BT-pcigar + const pcigar_t* const m_misms_bt_pcigar = wavefront_set->in_mwavefront_misms->bt_pcigar; + const pcigar_t* const m_open1_bt_pcigar = wavefront_set->in_mwavefront_open1->bt_pcigar; + const pcigar_t* const i1_ext_bt_pcigar = wavefront_set->in_i1wavefront_ext->bt_pcigar; + const pcigar_t* const d1_ext_bt_pcigar = wavefront_set->in_d1wavefront_ext->bt_pcigar; + // In BT-prev + const bt_block_idx_t* const m_misms_bt_prev = wavefront_set->in_mwavefront_misms->bt_prev; + const bt_block_idx_t* const m_open1_bt_prev = wavefront_set->in_mwavefront_open1->bt_prev; + const bt_block_idx_t* const i1_ext_bt_prev = wavefront_set->in_i1wavefront_ext->bt_prev; + const bt_block_idx_t* const d1_ext_bt_prev = wavefront_set->in_d1wavefront_ext->bt_prev; + // Out BT-pcigar + pcigar_t* const out_m_bt_pcigar = wavefront_set->out_mwavefront->bt_pcigar; + pcigar_t* const out_i1_bt_pcigar = wavefront_set->out_i1wavefront->bt_pcigar; + pcigar_t* const out_d1_bt_pcigar = wavefront_set->out_d1wavefront->bt_pcigar; + // Out BT-prev + bt_block_idx_t* const out_m_bt_prev = wavefront_set->out_mwavefront->bt_prev; + bt_block_idx_t* const out_i1_bt_prev = wavefront_set->out_i1wavefront->bt_prev; + bt_block_idx_t* const out_d1_bt_prev = wavefront_set->out_d1wavefront->bt_prev; + // Compute-Next kernel loop + int k; + PRAGMA_LOOP_VECTORIZE // Ifs predicated by the compiler + for (k=lo;k<=hi;++k) { + // Update I1 + const wf_offset_t ins1_o = m_open1[k-1]; + const wf_offset_t ins1_e = i1_ext[k-1]; + wf_offset_t ins1; + pcigar_t ins1_pcigar; + bt_block_idx_t ins1_block_idx; + if (ins1_e >= ins1_o) { + ins1 = ins1_e; + ins1_pcigar = i1_ext_bt_pcigar[k-1]; + ins1_block_idx = i1_ext_bt_prev[k-1]; + } else { + ins1 = ins1_o; + ins1_pcigar = m_open1_bt_pcigar[k-1]; + ins1_block_idx = m_open1_bt_prev[k-1]; + } + out_i1_bt_pcigar[k] = PCIGAR_PUSH_BACK_INS(ins1_pcigar); + out_i1_bt_prev[k] = ins1_block_idx; + out_i1[k] = ++ins1; + // Update D1 + const wf_offset_t del1_o = m_open1[k+1]; + const wf_offset_t del1_e = d1_ext[k+1]; + wf_offset_t del1; + pcigar_t del1_pcigar; + bt_block_idx_t del1_block_idx; + if (del1_e >= del1_o) { + del1 = del1_e; + del1_pcigar = d1_ext_bt_pcigar[k+1]; + del1_block_idx = d1_ext_bt_prev[k+1]; + } else { + del1 = del1_o; + del1_pcigar = m_open1_bt_pcigar[k+1]; + del1_block_idx = m_open1_bt_prev[k+1]; + } + out_d1_bt_pcigar[k] = PCIGAR_PUSH_BACK_DEL(del1_pcigar); + out_d1_bt_prev[k] = del1_block_idx; + out_d1[k] = del1; + // Update M + const wf_offset_t misms = m_misms[k] + 1; + wf_offset_t max = MAX(del1,MAX(misms,ins1)); + if (max == ins1) { + out_m_bt_pcigar[k] = out_i1_bt_pcigar[k]; + out_m_bt_prev[k] = out_i1_bt_prev[k]; + } + if (max == del1) { + out_m_bt_pcigar[k] = out_d1_bt_pcigar[k]; + out_m_bt_prev[k] = out_d1_bt_prev[k]; + } + if (max == misms) { + out_m_bt_pcigar[k] = m_misms_bt_pcigar[k]; + out_m_bt_prev[k] = m_misms_bt_prev[k]; + } + // Coming from I/D -> X is fake to represent gap-close + // Coming from M -> X is real to represent mismatch + out_m_bt_pcigar[k] = PCIGAR_PUSH_BACK_MISMS(out_m_bt_pcigar[k]); + // Adjust offset out of boundaries !(h>tlen,v>plen) (here to allow vectorization) + const wf_unsigned_offset_t h = WAVEFRONT_H(k,max); // Make unsigned to avoid checking negative + const wf_unsigned_offset_t v = WAVEFRONT_V(k,max); // Make unsigned to avoid checking negative + if (h > text_length) max = WAVEFRONT_OFFSET_NULL; + if (v > pattern_length) max = WAVEFRONT_OFFSET_NULL; + out_m[k] = max; + } +} +/* + * Compute Wavefronts (gap-affine) + */ +void wavefront_compute_affine_dispatcher( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Parameters + const bool bt_piggyback = wf_aligner->wf_components.bt_piggyback; + const int num_threads = wavefront_compute_num_threads(wf_aligner,lo,hi); + // Multithreading dispatcher + if (num_threads == 1) { + // Compute next wavefront + if (bt_piggyback) { + wavefront_compute_affine_idm_piggyback(wf_aligner,wavefront_set,lo,hi); + } else { + wavefront_compute_affine_idm(wf_aligner,wavefront_set,lo,hi); + } + } else { +#ifdef WFA_PARALLEL + // Compute next wavefront in parallel + #pragma omp parallel num_threads(num_threads) + { + int t_lo, t_hi; + const int thread_id = omp_get_thread_num(); + const int thread_num = omp_get_num_threads(); + wavefront_compute_thread_limits(thread_id,thread_num,lo,hi,&t_lo,&t_hi); + if (bt_piggyback) { + wavefront_compute_affine_idm_piggyback(wf_aligner,wavefront_set,t_lo,t_hi); + } else { + wavefront_compute_affine_idm(wf_aligner,wavefront_set,t_lo,t_hi); + } + } +#endif + } +} +void wavefront_compute_affine( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Select wavefronts + wavefront_set_t wavefront_set; + wavefront_compute_fetch_input(wf_aligner,&wavefront_set,score); + // Check null wavefronts + if (wavefront_set.in_mwavefront_misms->null && + wavefront_set.in_mwavefront_open1->null && + wavefront_set.in_i1wavefront_ext->null && + wavefront_set.in_d1wavefront_ext->null) { + wf_aligner->align_status.num_null_steps++; // Increment null-steps + wavefront_compute_allocate_output_null(wf_aligner,score); // Null s-wavefront + return; + } + wf_aligner->align_status.num_null_steps = 0; + // Set limits + int hi, lo; + wavefront_compute_limits_input(wf_aligner,&wavefront_set,&lo,&hi); + // Allocate wavefronts + wavefront_compute_allocate_output(wf_aligner,&wavefront_set,score,lo,hi); + // Init wavefront ends + wavefront_compute_init_ends(wf_aligner,&wavefront_set,lo,hi); + // Compute wavefronts + wavefront_compute_affine_dispatcher(wf_aligner,&wavefront_set,lo,hi); + // Offload backtrace (if necessary) + if (wf_aligner->wf_components.bt_piggyback) { + wavefront_backtrace_offload_affine(wf_aligner,&wavefront_set,lo,hi); + } + // Process wavefront ends + wavefront_compute_process_ends(wf_aligner,&wavefront_set,score); +} + + diff --git a/src/lib/wfa2/wavefront/wavefront_compute_affine.h b/src/lib/wfa2/wavefront/wavefront_compute_affine.h new file mode 100644 index 000000000..652d8ed52 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_compute_affine.h @@ -0,0 +1,58 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for computing wavefronts (gap-affine) + */ + +#ifndef WAVEFRONT_COMPUTE_AFFINE_H_ +#define WAVEFRONT_COMPUTE_AFFINE_H_ + +#include "wavefront_aligner.h" + +/* + * Compute Kernels + */ +void wavefront_compute_affine_idm( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi); +void wavefront_compute_affine_idm_piggyback( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi); + +/* + * Compute Wavefronts (gap-affine) + */ +void wavefront_compute_affine( + wavefront_aligner_t* const wf_aligner, + const int score); + +#endif /* WAVEFRONT_COMPUTE_AFFINE_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_compute_affine2p.c b/src/lib/wfa2/wavefront/wavefront_compute_affine2p.c new file mode 100644 index 000000000..5594f6c80 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_compute_affine2p.c @@ -0,0 +1,366 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for computing wavefronts (gap-affine-2p) + */ + +#include "../utils/string_padded.h" +#include "wavefront_compute.h" +#include "wavefront_compute_affine.h" +#include "wavefront_backtrace_offload.h" + +#ifdef WFA_PARALLEL +#include +#endif + +/* + * Compute Kernels + */ +void wavefront_compute_affine2p_idm( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + // In Offsets + const wf_offset_t* const m_misms = wavefront_set->in_mwavefront_misms->offsets; + const wf_offset_t* const m_open1 = wavefront_set->in_mwavefront_open1->offsets; + const wf_offset_t* const m_open2 = wavefront_set->in_mwavefront_open2->offsets; + const wf_offset_t* const i1_ext = wavefront_set->in_i1wavefront_ext->offsets; + const wf_offset_t* const i2_ext = wavefront_set->in_i2wavefront_ext->offsets; + const wf_offset_t* const d1_ext = wavefront_set->in_d1wavefront_ext->offsets; + const wf_offset_t* const d2_ext = wavefront_set->in_d2wavefront_ext->offsets; + // Out Offsets + wf_offset_t* const out_m = wavefront_set->out_mwavefront->offsets; + wf_offset_t* const out_i1 = wavefront_set->out_i1wavefront->offsets; + wf_offset_t* const out_i2 = wavefront_set->out_i2wavefront->offsets; + wf_offset_t* const out_d1 = wavefront_set->out_d1wavefront->offsets; + wf_offset_t* const out_d2 = wavefront_set->out_d2wavefront->offsets; + // Compute-Next kernel loop + int k; + PRAGMA_LOOP_VECTORIZE + for (k=lo;k<=hi;++k) { + // Update I1 + const wf_offset_t ins1_o = m_open1[k-1]; + const wf_offset_t ins1_e = i1_ext[k-1]; + const wf_offset_t ins1 = MAX(ins1_o,ins1_e) + 1; + out_i1[k] = ins1; + // Update I2 + const wf_offset_t ins2_o = m_open2[k-1]; + const wf_offset_t ins2_e = i2_ext[k-1]; + const wf_offset_t ins2 = MAX(ins2_o,ins2_e) + 1; + out_i2[k] = ins2; + // Update I + const wf_offset_t ins = MAX(ins1,ins2); + // Update D1 + const wf_offset_t del1_o = m_open1[k+1]; + const wf_offset_t del1_e = d1_ext[k+1]; + const wf_offset_t del1 = MAX(del1_o,del1_e); + out_d1[k] = del1; + // Update D2 + const wf_offset_t del2_o = m_open2[k+1]; + const wf_offset_t del2_e = d2_ext[k+1]; + const wf_offset_t del2 = MAX(del2_o,del2_e); + out_d2[k] = del2; + // Update D + const wf_offset_t del = MAX(del1,del2); + // Update M + const wf_offset_t misms = m_misms[k] + 1; + wf_offset_t max = MAX(del,MAX(misms,ins)); + // Adjust offset out of boundaries !(h>tlen,v>plen) (here to allow vectorization) + const wf_unsigned_offset_t h = WAVEFRONT_H(k,max); // Make unsigned to avoid checking negative + const wf_unsigned_offset_t v = WAVEFRONT_V(k,max); // Make unsigned to avoid checking negative + if (h > text_length) max = WAVEFRONT_OFFSET_NULL; + if (v > pattern_length) max = WAVEFRONT_OFFSET_NULL; + out_m[k] = max; + } +} +/* + * Compute Kernel (Piggyback) + */ +void wavefront_compute_affine2p_idm_piggyback( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + // In Offsets + const wf_offset_t* const m_misms = wavefront_set->in_mwavefront_misms->offsets; + const wf_offset_t* const m_open1 = wavefront_set->in_mwavefront_open1->offsets; + const wf_offset_t* const m_open2 = wavefront_set->in_mwavefront_open2->offsets; + const wf_offset_t* const i1_ext = wavefront_set->in_i1wavefront_ext->offsets; + const wf_offset_t* const i2_ext = wavefront_set->in_i2wavefront_ext->offsets; + const wf_offset_t* const d1_ext = wavefront_set->in_d1wavefront_ext->offsets; + const wf_offset_t* const d2_ext = wavefront_set->in_d2wavefront_ext->offsets; + // Out Offsets + wf_offset_t* const out_m = wavefront_set->out_mwavefront->offsets; + wf_offset_t* const out_i1 = wavefront_set->out_i1wavefront->offsets; + wf_offset_t* const out_i2 = wavefront_set->out_i2wavefront->offsets; + wf_offset_t* const out_d1 = wavefront_set->out_d1wavefront->offsets; + wf_offset_t* const out_d2 = wavefront_set->out_d2wavefront->offsets; + // In BT-pcigar + const pcigar_t* const m_misms_bt_pcigar = wavefront_set->in_mwavefront_misms->bt_pcigar; + const pcigar_t* const m_open1_bt_pcigar = wavefront_set->in_mwavefront_open1->bt_pcigar; + const pcigar_t* const m_open2_bt_pcigar = wavefront_set->in_mwavefront_open2->bt_pcigar; + const pcigar_t* const i1_ext_bt_pcigar = wavefront_set->in_i1wavefront_ext->bt_pcigar; + const pcigar_t* const i2_ext_bt_pcigar = wavefront_set->in_i2wavefront_ext->bt_pcigar; + const pcigar_t* const d1_ext_bt_pcigar = wavefront_set->in_d1wavefront_ext->bt_pcigar; + const pcigar_t* const d2_ext_bt_pcigar = wavefront_set->in_d2wavefront_ext->bt_pcigar; + // In BT-prev + const bt_block_idx_t* const m_misms_bt_prev = wavefront_set->in_mwavefront_misms->bt_prev; + const bt_block_idx_t* const m_open1_bt_prev = wavefront_set->in_mwavefront_open1->bt_prev; + const bt_block_idx_t* const m_open2_bt_prev = wavefront_set->in_mwavefront_open2->bt_prev; + const bt_block_idx_t* const i1_ext_bt_prev = wavefront_set->in_i1wavefront_ext->bt_prev; + const bt_block_idx_t* const i2_ext_bt_prev = wavefront_set->in_i2wavefront_ext->bt_prev; + const bt_block_idx_t* const d1_ext_bt_prev = wavefront_set->in_d1wavefront_ext->bt_prev; + const bt_block_idx_t* const d2_ext_bt_prev = wavefront_set->in_d2wavefront_ext->bt_prev; + // Out BT-pcigar + pcigar_t* const out_m_bt_pcigar = wavefront_set->out_mwavefront->bt_pcigar; + pcigar_t* const out_i1_bt_pcigar = wavefront_set->out_i1wavefront->bt_pcigar; + pcigar_t* const out_i2_bt_pcigar = wavefront_set->out_i2wavefront->bt_pcigar; + pcigar_t* const out_d1_bt_pcigar = wavefront_set->out_d1wavefront->bt_pcigar; + pcigar_t* const out_d2_bt_pcigar = wavefront_set->out_d2wavefront->bt_pcigar; + // Out BT-prev + bt_block_idx_t* const out_m_bt_prev = wavefront_set->out_mwavefront->bt_prev; + bt_block_idx_t* const out_i1_bt_prev = wavefront_set->out_i1wavefront->bt_prev; + bt_block_idx_t* const out_i2_bt_prev = wavefront_set->out_i2wavefront->bt_prev; + bt_block_idx_t* const out_d1_bt_prev = wavefront_set->out_d1wavefront->bt_prev; + bt_block_idx_t* const out_d2_bt_prev = wavefront_set->out_d2wavefront->bt_prev; + // Compute-Next kernel loop + int k; + PRAGMA_LOOP_VECTORIZE // Ifs predicated by the compiler + for (k=lo;k<=hi;++k) { + /* + * I-Block + */ + // Update I1 + const wf_offset_t ins1_o = m_open1[k-1]; + const wf_offset_t ins1_e = i1_ext[k-1]; + wf_offset_t ins1; + pcigar_t ins1_pcigar; + bt_block_idx_t ins1_block_idx; + if (ins1_e >= ins1_o) { + ins1 = ins1_e; + ins1_pcigar = i1_ext_bt_pcigar[k-1]; + ins1_block_idx = i1_ext_bt_prev[k-1]; + } else { + ins1 = ins1_o; + ins1_pcigar = m_open1_bt_pcigar[k-1]; + ins1_block_idx = m_open1_bt_prev[k-1]; + } + out_i1_bt_pcigar[k] = PCIGAR_PUSH_BACK_INS(ins1_pcigar); + out_i1_bt_prev[k] = ins1_block_idx; + out_i1[k] = ++ins1; + // Update I2 + const wf_offset_t ins2_o = m_open2[k-1]; + const wf_offset_t ins2_e = i2_ext[k-1]; + wf_offset_t ins2; + pcigar_t ins2_pcigar; + bt_block_idx_t ins2_block_idx; + if (ins2_e >= ins2_o) { + ins2 = ins2_e; + ins2_pcigar = i2_ext_bt_pcigar[k-1]; + ins2_block_idx = i2_ext_bt_prev[k-1]; + } else { + ins2 = ins2_o; + ins2_pcigar = m_open2_bt_pcigar[k-1]; + ins2_block_idx = m_open2_bt_prev[k-1]; + } + out_i2_bt_pcigar[k] = PCIGAR_PUSH_BACK_INS(ins2_pcigar); + out_i2_bt_prev[k] = ins2_block_idx; + out_i2[k] = ++ins2; + // Update I + const wf_offset_t ins = MAX(ins1,ins2); + /* + * D-Block + */ + // Update D1 + const wf_offset_t del1_o = m_open1[k+1]; + const wf_offset_t del1_e = d1_ext[k+1]; + wf_offset_t del1; + pcigar_t del1_pcigar; + bt_block_idx_t del1_block_idx; + if (del1_e >= del1_o) { + del1 = del1_e; + del1_pcigar = d1_ext_bt_pcigar[k+1]; + del1_block_idx = d1_ext_bt_prev[k+1]; + } else { + del1 = del1_o; + del1_pcigar = m_open1_bt_pcigar[k+1]; + del1_block_idx = m_open1_bt_prev[k+1]; + } + out_d1_bt_pcigar[k] = PCIGAR_PUSH_BACK_DEL(del1_pcigar); + out_d1_bt_prev[k] = del1_block_idx; + out_d1[k] = del1; + // Update D2 + const wf_offset_t del2_o = m_open2[k+1]; + const wf_offset_t del2_e = d2_ext[k+1]; + wf_offset_t del2; + pcigar_t del2_pcigar; + bt_block_idx_t del2_block_idx; + if (del2_e >= del2_o) { + del2 = del2_e; + del2_pcigar = d2_ext_bt_pcigar[k+1]; + del2_block_idx = d2_ext_bt_prev[k+1]; + } else { + del2 = del2_o; + del2_pcigar = m_open2_bt_pcigar[k+1]; + del2_block_idx = m_open2_bt_prev[k+1]; + } + out_d2_bt_pcigar[k] = PCIGAR_PUSH_BACK_DEL(del2_pcigar); + out_d2_bt_prev[k] = del2_block_idx; + out_d2[k] = del2; + // Update D + const wf_offset_t del = MAX(del1,del2); + /* + * M-Block + */ + const wf_offset_t misms = m_misms[k] + 1; + wf_offset_t max = MAX(del,MAX(misms,ins)); + if (max == ins1) { + out_m_bt_pcigar[k] = out_i1_bt_pcigar[k]; + out_m_bt_prev[k] = out_i1_bt_prev[k]; + } + if (max == ins2) { + out_m_bt_pcigar[k] = out_i2_bt_pcigar[k]; + out_m_bt_prev[k] = out_i2_bt_prev[k]; + } + if (max == del1) { + out_m_bt_pcigar[k] = out_d1_bt_pcigar[k]; + out_m_bt_prev[k] = out_d1_bt_prev[k]; + } + if (max == del2) { + out_m_bt_pcigar[k] = out_d2_bt_pcigar[k]; + out_m_bt_prev[k] = out_d2_bt_prev[k]; + } + if (max == misms) { + out_m_bt_pcigar[k] = m_misms_bt_pcigar[k]; + out_m_bt_prev[k] = m_misms_bt_prev[k]; + } + // Coming from I/D -> X is fake to represent gap-close + // Coming from M -> X is real to represent mismatch + out_m_bt_pcigar[k] = PCIGAR_PUSH_BACK_MISMS(out_m_bt_pcigar[k]); + // Adjust offset out of boundaries !(h>tlen,v>plen) (here to allow vectorization) + const wf_unsigned_offset_t h = WAVEFRONT_H(k,max); // Make unsigned to avoid checking negative + const wf_unsigned_offset_t v = WAVEFRONT_V(k,max); // Make unsigned to avoid checking negative + if (h > text_length) max = WAVEFRONT_OFFSET_NULL; + if (v > pattern_length) max = WAVEFRONT_OFFSET_NULL; + out_m[k] = max; + } +} +/* + * Compute wavefronts + */ +void wavefront_compute_affine2p_dispatcher( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + if (wavefront_set->in_mwavefront_open2->null && + wavefront_set->in_i2wavefront_ext->null && + wavefront_set->in_d2wavefront_ext->null) { + // Delegate to regular gap-affine + if (wf_aligner->wf_components.bt_piggyback) { + wavefront_compute_affine_idm_piggyback(wf_aligner,wavefront_set,lo,hi); + } else { + wavefront_compute_affine_idm(wf_aligner,wavefront_set,lo,hi); + } + } else { + // Full gap-affine-2p + if (wf_aligner->wf_components.bt_piggyback) { + wavefront_compute_affine2p_idm_piggyback(wf_aligner,wavefront_set,lo,hi); + } else { + wavefront_compute_affine2p_idm(wf_aligner,wavefront_set,lo,hi); + } + } +} +void wavefront_compute_affine2p_dispatcher_omp( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Parameters + const int num_threads = wavefront_compute_num_threads(wf_aligner,lo,hi); + // Multithreading dispatcher + if (num_threads == 1) { + // Compute next wavefront + wavefront_compute_affine2p_dispatcher(wf_aligner,wavefront_set,lo,hi); + } else { +#ifdef WFA_PARALLEL + // Compute next wavefront in parallel + #pragma omp parallel num_threads(num_threads) + { + int t_lo, t_hi; + const int thread_id = omp_get_thread_num(); + const int thread_num = omp_get_num_threads(); + wavefront_compute_thread_limits(thread_id,thread_num,lo,hi,&t_lo,&t_hi); + wavefront_compute_affine2p_dispatcher(wf_aligner,wavefront_set,t_lo,t_hi); + } +#endif + } +} +void wavefront_compute_affine2p( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Select wavefronts + wavefront_set_t wavefront_set; + wavefront_compute_fetch_input(wf_aligner,&wavefront_set,score); + // Check null wavefronts + if (wavefront_set.in_mwavefront_misms->null && + wavefront_set.in_mwavefront_open1->null && + wavefront_set.in_mwavefront_open2->null && + wavefront_set.in_i1wavefront_ext->null && + wavefront_set.in_i2wavefront_ext->null && + wavefront_set.in_d1wavefront_ext->null && + wavefront_set.in_d2wavefront_ext->null) { + wf_aligner->align_status.num_null_steps++; // Increment null-steps + wavefront_compute_allocate_output_null(wf_aligner,score); // Null s-wavefront + return; + } + wf_aligner->align_status.num_null_steps = 0; + // Set limits + int hi, lo; + wavefront_compute_limits_input(wf_aligner,&wavefront_set,&lo,&hi); + // Allocate wavefronts + wavefront_compute_allocate_output(wf_aligner,&wavefront_set,score,lo,hi); + // Init wavefront ends + wavefront_compute_init_ends(wf_aligner,&wavefront_set,lo,hi); + // Compute wavefronts + wavefront_compute_affine2p_dispatcher_omp(wf_aligner,&wavefront_set,lo,hi); + // Offload backtrace (if necessary) + if (wf_aligner->wf_components.bt_piggyback) { + wavefront_backtrace_offload_affine(wf_aligner,&wavefront_set,lo,hi); + } + // Process wavefront ends + wavefront_compute_process_ends(wf_aligner,&wavefront_set,score); +} + diff --git a/src/lib/wfa2/wavefront/wavefront_compute_affine2p.h b/src/lib/wfa2/wavefront/wavefront_compute_affine2p.h new file mode 100644 index 000000000..fbb64df85 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_compute_affine2p.h @@ -0,0 +1,44 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for computing wavefronts (gap-affine-2p) + */ + +#ifndef WAVEFRONT_COMPUTE_AFFINE2P_H_ +#define WAVEFRONT_COMPUTE_AFFINE2P_H_ + +#include "wavefront_aligner.h" + +/* + * Compute Wavefronts (gap-affine-2p) + */ +void wavefront_compute_affine2p( + wavefront_aligner_t* const wf_aligner, + const int score); + +#endif /* WAVEFRONT_COMPUTE_AFFINE2P_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_compute_edit.c b/src/lib/wfa2/wavefront/wavefront_compute_edit.c new file mode 100644 index 000000000..0747973de --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_compute_edit.c @@ -0,0 +1,370 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for computing wavefronts (edit/indel) + */ + +#include "../utils/string_padded.h" +#include "wavefront_compute.h" +#include "wavefront_backtrace_offload.h" + +#ifdef WFA_PARALLEL +#include +#endif + +/* + * Compute Kernels + */ +void wavefront_compute_indel_idm( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wf_prev, + wavefront_t* const wf_curr, + const int lo, + const int hi) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + const wf_offset_t* const prev_offsets = wf_prev->offsets; + wf_offset_t* const curr_offsets = wf_curr->offsets; + // Compute-Next kernel loop + int k; + PRAGMA_LOOP_VECTORIZE + for (k=lo;k<=hi;++k) { + // Compute maximum offset + const wf_offset_t ins = prev_offsets[k-1] + 1; + const wf_offset_t del = prev_offsets[k+1]; + wf_offset_t max = MAX(del,ins); + // Adjust offset out of boundaries !(h>tlen,v>plen) (here to allow vectorization) + const wf_unsigned_offset_t h = WAVEFRONT_H(k,max); // Make unsigned to avoid checking negative + const wf_unsigned_offset_t v = WAVEFRONT_V(k,max); // Make unsigned to avoid checking negative + if (h > text_length) max = WAVEFRONT_OFFSET_NULL; + if (v > pattern_length) max = WAVEFRONT_OFFSET_NULL; + curr_offsets[k] = max; + } +} +void wavefront_compute_edit_idm( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wf_prev, + wavefront_t* const wf_curr, + const int lo, + const int hi) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + const wf_offset_t* const prev_offsets = wf_prev->offsets; + wf_offset_t* const curr_offsets = wf_curr->offsets; + // Compute-Next kernel loop + int k; + PRAGMA_LOOP_VECTORIZE + for (k=lo;k<=hi;++k) { + // Compute maximum offset + const wf_offset_t ins = prev_offsets[k-1]; // Lower + const wf_offset_t del = prev_offsets[k+1]; // Upper + const wf_offset_t misms = prev_offsets[k]; // Mid + wf_offset_t max = MAX(del,MAX(ins,misms)+1); + // Adjust offset out of boundaries !(h>tlen,v>plen) (here to allow vectorization) + const wf_unsigned_offset_t h = WAVEFRONT_H(k,max); // Make unsigned to avoid checking negative + const wf_unsigned_offset_t v = WAVEFRONT_V(k,max); // Make unsigned to avoid checking negative + if (h > text_length) max = WAVEFRONT_OFFSET_NULL; + if (v > pattern_length) max = WAVEFRONT_OFFSET_NULL; + curr_offsets[k] = max; + } +} +/* + * Compute Kernel (Piggyback) + */ +void wavefront_compute_indel_idm_piggyback( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wf_prev, + wavefront_t* const wf_curr, + const int lo, + const int hi, + const int score) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + // Previous WF + const wf_offset_t* const prev_offsets = wf_prev->offsets; + const pcigar_t* const prev_pcigar = wf_prev->bt_pcigar; + const bt_block_idx_t* const prev_bt_idx = wf_prev->bt_prev; + // Current WF + wf_offset_t* const curr_offsets = wf_curr->offsets; + pcigar_t* const curr_pcigar = wf_curr->bt_pcigar; + bt_block_idx_t* const curr_bt_idx = wf_curr->bt_prev; + // Compute-Next kernel loop + int k; + PRAGMA_LOOP_VECTORIZE // Ifs predicated by the compiler + for (k=lo;k<=hi;++k) { + // Compute maximum offset + const wf_offset_t ins = prev_offsets[k-1] + 1; + const wf_offset_t del = prev_offsets[k+1]; + wf_offset_t max = MAX(del,ins); + // Update pcigar & bt-block + if (max == del) { + curr_pcigar[k] = PCIGAR_PUSH_BACK_DEL(prev_pcigar[k+1]); + curr_bt_idx[k] = prev_bt_idx[k+1]; + } else { // max == ins + curr_pcigar[k] = PCIGAR_PUSH_BACK_INS(prev_pcigar[k-1]); + curr_bt_idx[k] = prev_bt_idx[k-1]; + } + // Adjust offset out of boundaries !(h>tlen,v>plen) (here to allow vectorization) + const wf_unsigned_offset_t h = WAVEFRONT_H(k,max); // Make unsigned to avoid checking negative + const wf_unsigned_offset_t v = WAVEFRONT_V(k,max); // Make unsigned to avoid checking negative + if (h > text_length) max = WAVEFRONT_OFFSET_NULL; + if (v > pattern_length) max = WAVEFRONT_OFFSET_NULL; + curr_offsets[k] = max; + } +} +void wavefront_compute_edit_idm_piggyback( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wf_prev, + wavefront_t* const wf_curr, + const int lo, + const int hi, + const int score) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + // Previous WF + const wf_offset_t* const prev_offsets = wf_prev->offsets; + const pcigar_t* const prev_pcigar = wf_prev->bt_pcigar; + const bt_block_idx_t* const prev_bt_idx = wf_prev->bt_prev; + // Current WF + wf_offset_t* const curr_offsets = wf_curr->offsets; + pcigar_t* const curr_pcigar = wf_curr->bt_pcigar; + bt_block_idx_t* const curr_bt_idx = wf_curr->bt_prev; + // Compute-Next kernel loop + int k; + PRAGMA_LOOP_VECTORIZE // Ifs predicated by the compiler + for (k=lo;k<=hi;++k) { + // Compute maximum offset + const wf_offset_t ins = prev_offsets[k-1] + 1; // Lower + const wf_offset_t del = prev_offsets[k+1]; // Upper + const wf_offset_t misms = prev_offsets[k] + 1; // Mid + wf_offset_t max = MAX(del,MAX(ins,misms)); + // Update pcigar & bt-block + if (max == ins) { + curr_pcigar[k] = PCIGAR_PUSH_BACK_INS(prev_pcigar[k-1]); + curr_bt_idx[k] = prev_bt_idx[k-1]; + } + if (max == del) { + curr_pcigar[k] = PCIGAR_PUSH_BACK_DEL(prev_pcigar[k+1]); + curr_bt_idx[k] = prev_bt_idx[k+1]; + } + if (max == misms) { + curr_pcigar[k] = PCIGAR_PUSH_BACK_MISMS(prev_pcigar[k]); + curr_bt_idx[k] = prev_bt_idx[k]; + } + // Adjust offset out of boundaries !(h>tlen,v>plen) (here to allow vectorization) + const wf_unsigned_offset_t h = WAVEFRONT_H(k,max); // Make unsigned to avoid checking negative + const wf_unsigned_offset_t v = WAVEFRONT_V(k,max); // Make unsigned to avoid checking negative + if (h > text_length) max = WAVEFRONT_OFFSET_NULL; + if (v > pattern_length) max = WAVEFRONT_OFFSET_NULL; + curr_offsets[k] = max; + } +} +/* + * Exact pruning paths + */ +int wf_compute_edit_best_score( + const int pattern_length, + const int text_length, + const int k, + const wf_offset_t offset) { + // Compute best-alignment case + const int left_v = pattern_length - WAVEFRONT_V(k,offset); + const int left_h = text_length - WAVEFRONT_H(k,offset); + return (left_v >= left_h) ? left_v - left_h : left_h - left_v; +} +int wf_compute_edit_worst_score( + const int pattern_length, + const int text_length, + const int k, + const wf_offset_t offset) { + // Compute worst-alignment case + const int left_v = pattern_length - WAVEFRONT_V(k,offset); + const int left_h = text_length - WAVEFRONT_H(k,offset); + return MAX(left_v,left_h); +} +void wavefront_compute_edit_exact_prune( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront) { + // Parameters + const int plen = wf_aligner->pattern_length; + const int tlen = wf_aligner->text_length; + wf_offset_t* const offsets = wavefront->offsets; + const int lo = wavefront->lo; + const int hi = wavefront->hi; + // Speculative compute if needed + if (WAVEFRONT_LENGTH(lo,hi) < 1000) return; + const int sample_k = lo + (hi-lo)/2; + const wf_offset_t sample_offset = offsets[sample_k]; + if (sample_offset < 0) return; // Unlucky null in the middle + const int smax_sample = wf_compute_edit_worst_score(plen,tlen,sample_k,offsets[sample_k]); + const int smin_lo = wf_compute_edit_best_score(plen,tlen,lo,offsets[lo]); + const int smin_hi = wf_compute_edit_best_score(plen,tlen,hi,offsets[hi]); + if (smin_lo <= smax_sample && smin_hi <= smax_sample) return; + /* + * Suggested by Heng Li as an effective exact-prunning technique + * for sequences of very different length where some diagonals + * can be proven impossible to yield better alignments. + */ + // Compute the best worst-case-alignment + int score_min_worst = INT_MAX; + int k; + for (k=lo;k<=hi;++k) { + const wf_offset_t offset = offsets[k]; + if (offset < 0) continue; // Skip nulls + // Compute worst-alignment case + const int score_worst = wf_compute_edit_worst_score(plen,tlen,k,offset); + if (score_worst < score_min_worst) score_min_worst = score_worst; + } + // Compare against the best-case-alignment (Prune from bottom) + int lo_reduced = lo; + for (k=lo;k<=hi;++k) { + // Compute best-alignment case + const wf_offset_t offset = offsets[k]; + const int score_best = wf_compute_edit_best_score(plen,tlen,k,offset); + // Compare best and worst + if (score_best <= score_min_worst) break; + ++lo_reduced; + } + wavefront->lo = lo_reduced; + // Compare against the best-case-alignment (Prune from top) + int hi_reduced = hi; + for (k=hi;k>lo_reduced;--k) { + // Compute best-alignment case + const wf_offset_t offset = offsets[k]; + const int score_best = wf_compute_edit_best_score(plen,tlen,k,offset); + // Compare best and worst + if (score_best <= score_min_worst) break; + --hi_reduced; + } + wavefront->hi = hi_reduced; +} +/* + * Compute next wavefront + */ +void wavefront_compute_edit_dispatcher( + wavefront_aligner_t* const wf_aligner, + const int score, + wavefront_t* const wf_prev, + wavefront_t* const wf_curr, + const int lo, + const int hi) { + if (wf_aligner->wf_components.bt_piggyback) { + if (wf_aligner->penalties.distance_metric == indel) { + wavefront_compute_indel_idm_piggyback(wf_aligner,wf_prev,wf_curr,lo,hi,score); + } else { + wavefront_compute_edit_idm_piggyback(wf_aligner,wf_prev,wf_curr,lo,hi,score); + } + } else { + if (wf_aligner->penalties.distance_metric == indel) { + wavefront_compute_indel_idm(wf_aligner,wf_prev,wf_curr,lo,hi); + } else { + wavefront_compute_edit_idm(wf_aligner,wf_prev,wf_curr,lo,hi); + } + } +} +void wavefront_compute_edit_dispatcher_omp( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wf_prev, + wavefront_t* const wf_curr, + const int lo, + const int hi, + const int score) { + // Parameters + const int num_threads = wavefront_compute_num_threads(wf_aligner,lo,hi); + // Multithreading dispatcher + if (num_threads == 1) { + // Compute next wavefront + wavefront_compute_edit_dispatcher( + wf_aligner,score,wf_prev,wf_curr,lo,hi); + } else { +#ifdef WFA_PARALLEL + // Compute next wavefront in parallel + #pragma omp parallel num_threads(num_threads) + { + int t_lo, t_hi; + const int thread_id = omp_get_thread_num(); + const int thread_num = omp_get_num_threads(); + wavefront_compute_thread_limits(thread_id,thread_num,lo,hi,&t_lo,&t_hi); + wavefront_compute_edit_dispatcher( + wf_aligner,score,wf_prev,wf_curr,t_lo,t_hi); + } +#endif + } +} +void wavefront_compute_edit( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + // Compute scores + int score_prev = score - 1; + int score_curr = score; + if (wf_components->memory_modular) { // Modular wavefront + score_prev = score_prev % wf_components->max_score_scope; + score_curr = score_curr % wf_components->max_score_scope; + if (wf_components->mwavefronts[score_curr]) { // Free + wavefront_slab_free(wf_aligner->wavefront_slab,wf_components->mwavefronts[score_curr]); + } + } + // Fetch previous wavefront, compute limits & initialize + wavefront_t* const wf_prev = wf_components->mwavefronts[score_prev]; + const int lo = wf_prev->lo - 1; + const int hi = wf_prev->hi + 1; + // wf_components->historic_min_lo = min_lo; + // wf_components->historic_max_hi = max_hi; + wf_prev->offsets[lo-1] = WAVEFRONT_OFFSET_NULL; + wf_prev->offsets[lo] = WAVEFRONT_OFFSET_NULL; + wf_prev->offsets[hi] = WAVEFRONT_OFFSET_NULL; + wf_prev->offsets[hi+1] = WAVEFRONT_OFFSET_NULL; + // Allocate output wavefront + wavefront_t* const wf_curr = wavefront_slab_allocate(wf_aligner->wavefront_slab,lo-2,hi+2); + wf_components->mwavefronts[score_curr] = wf_curr; + wf_components->mwavefronts[score_curr]->lo = lo; + wf_components->mwavefronts[score_curr]->hi = hi; + // Compute Wavefront + wavefront_compute_edit_dispatcher_omp(wf_aligner,wf_prev,wf_curr,lo,hi,score); + // Offload backtrace (if necessary) + if (wf_components->bt_piggyback && score % PCIGAR_MAX_LENGTH == 0) { + wavefront_backtrace_offload_blocks_linear( + wf_aligner,wf_curr->offsets,wf_curr->bt_pcigar,wf_curr->bt_prev,lo,hi); + } + // Trim wavefront ends + wavefront_compute_trim_ends(wf_aligner,wf_curr); + if (wf_curr->null) wf_aligner->align_status.num_null_steps = INT_MAX; + // Exact pruning paths + if (wf_aligner->alignment_form.span == alignment_end2end && + wf_aligner->penalties.distance_metric == edit) { + wavefront_compute_edit_exact_prune(wf_aligner,wf_curr); + } +} + + diff --git a/src/lib/wfa2/wavefront/wavefront_compute_edit.h b/src/lib/wfa2/wavefront/wavefront_compute_edit.h new file mode 100644 index 000000000..0fe06b256 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_compute_edit.h @@ -0,0 +1,44 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for computing wavefronts (edit/indel) + */ + +#ifndef WAVEFRONT_COMPUTE_EDIT_H_ +#define WAVEFRONT_COMPUTE_EDIT_H_ + +#include "wavefront_aligner.h" + +/* + * Compute wavefront (edit) + */ +void wavefront_compute_edit( + wavefront_aligner_t* const wf_aligner, + const int score); + +#endif /* WAVEFRONT_COMPUTE_EDIT_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_compute_linear.c b/src/lib/wfa2/wavefront/wavefront_compute_linear.c new file mode 100644 index 000000000..cd95f71cb --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_compute_linear.c @@ -0,0 +1,194 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for computing wavefronts (gap-linear) + */ + +#include "../utils/string_padded.h" +#include "wavefront_compute.h" +#include "wavefront_backtrace_offload.h" + +#ifdef WFA_PARALLEL +#include +#endif + +/* + * Compute Kernels + */ +void wavefront_compute_linear_idm( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + // In Offsets + const wf_offset_t* const m_misms = wavefront_set->in_mwavefront_misms->offsets; + const wf_offset_t* const m_open1 = wavefront_set->in_mwavefront_open1->offsets; + // Out Offsets + wf_offset_t* const out_m = wavefront_set->out_mwavefront->offsets; + // Compute-Next kernel loop + int k; + PRAGMA_LOOP_VECTORIZE + for (k=lo;k<=hi;++k) { + // Compute maximum Offset + const wf_offset_t ins1 = m_open1[k-1]; + const wf_offset_t del1 = m_open1[k+1]; + const wf_offset_t misms = m_misms[k]; + wf_offset_t max = MAX(del1,MAX(misms,ins1)+1); + // Adjust offset out of boundaries !(h>tlen,v>plen) (here to allow vectorization) + const wf_unsigned_offset_t h = WAVEFRONT_H(k,max); // Make unsigned to avoid checking negative + const wf_unsigned_offset_t v = WAVEFRONT_V(k,max); // Make unsigned to avoid checking negative + if (h > text_length) max = WAVEFRONT_OFFSET_NULL; + if (v > pattern_length) max = WAVEFRONT_OFFSET_NULL; + out_m[k] = max; + } +} +/* + * Compute Kernel (Piggyback) + */ +void wavefront_compute_linear_idm_piggyback( + wavefront_aligner_t* const wf_aligner, + const wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + // In M + const wf_offset_t* const m_misms = wavefront_set->in_mwavefront_misms->offsets; + const pcigar_t* const m_misms_bt_pcigar = wavefront_set->in_mwavefront_misms->bt_pcigar; + const bt_block_idx_t* const m_misms_bt_prev = wavefront_set->in_mwavefront_misms->bt_prev; + // In I/D + const wf_offset_t* const m_open1 = wavefront_set->in_mwavefront_open1->offsets; + const pcigar_t* const m_open1_bt_pcigar = wavefront_set->in_mwavefront_open1->bt_pcigar; + const bt_block_idx_t* const m_open1_bt_prev = wavefront_set->in_mwavefront_open1->bt_prev; + // Out + wf_offset_t* const out_m = wavefront_set->out_mwavefront->offsets; + pcigar_t* const out_m_bt_pcigar = wavefront_set->out_mwavefront->bt_pcigar; + bt_block_idx_t* const out_m_bt_prev = wavefront_set->out_mwavefront->bt_prev; + // Compute-Next kernel loop + int k; + PRAGMA_LOOP_VECTORIZE // Ifs predicated by the compiler + for (k=lo;k<=hi;++k) { + // Compute maximum Offset + const wf_offset_t ins1 = m_open1[k-1] + 1; + const wf_offset_t del1 = m_open1[k+1]; + const wf_offset_t misms = m_misms[k] + 1; + wf_offset_t max = MAX(del1,MAX(misms,ins1)); + // Update pcigar & bt-block + if (max == ins1) { + out_m_bt_pcigar[k] = PCIGAR_PUSH_BACK_INS(m_open1_bt_pcigar[k-1]); + out_m_bt_prev[k] = m_open1_bt_prev[k-1]; + } + if (max == del1) { + out_m_bt_pcigar[k] = PCIGAR_PUSH_BACK_DEL(m_open1_bt_pcigar[k+1]); + out_m_bt_prev[k] = m_open1_bt_prev[k+1]; + } + if (max == misms) { + out_m_bt_pcigar[k] = PCIGAR_PUSH_BACK_MISMS(m_misms_bt_pcigar[k]); + out_m_bt_prev[k] = m_misms_bt_prev[k]; + } + // Adjust offset out of boundaries !(h>tlen,v>plen) (here to allow vectorization) + const wf_unsigned_offset_t h = WAVEFRONT_H(k,max); // Make unsigned to avoid checking negative + const wf_unsigned_offset_t v = WAVEFRONT_V(k,max); // Make unsigned to avoid checking negative + if (h > text_length) max = WAVEFRONT_OFFSET_NULL; + if (v > pattern_length) max = WAVEFRONT_OFFSET_NULL; + out_m[k] = max; + } +} +/* + * Compute Wavefronts (gap-linear) + */ +void wavefront_compute_linear_dispatcher( + wavefront_aligner_t* const wf_aligner, + wavefront_set_t* const wavefront_set, + const int lo, + const int hi) { + // Parameters + const bool bt_piggyback = wf_aligner->wf_components.bt_piggyback; + const int num_threads = wavefront_compute_num_threads(wf_aligner,lo,hi); + // Multithreading dispatcher + if (num_threads == 1) { + // Compute next wavefront + if (bt_piggyback) { + wavefront_compute_linear_idm_piggyback(wf_aligner,wavefront_set,lo,hi); + } else { + wavefront_compute_linear_idm(wf_aligner,wavefront_set,lo,hi); + } + } else { +#ifdef WFA_PARALLEL + // Compute next wavefront in parallel + #pragma omp parallel num_threads(num_threads) + { + int t_lo, t_hi; + const int thread_id = omp_get_thread_num(); + const int thread_num = omp_get_num_threads(); + wavefront_compute_thread_limits(thread_id,thread_num,lo,hi,&t_lo,&t_hi); + if (bt_piggyback) { + wavefront_compute_linear_idm_piggyback(wf_aligner,wavefront_set,t_lo,t_hi); + } else { + wavefront_compute_linear_idm(wf_aligner,wavefront_set,t_lo,t_hi); + } + } +#endif + } +} +void wavefront_compute_linear( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Select wavefronts + wavefront_set_t wavefront_set; + wavefront_compute_fetch_input(wf_aligner,&wavefront_set,score); + // Check null wavefronts + if (wavefront_set.in_mwavefront_misms->null && + wavefront_set.in_mwavefront_open1->null) { + wf_aligner->align_status.num_null_steps++; // Increment null-steps + wavefront_compute_allocate_output_null(wf_aligner,score); // Null s-wavefront + return; + } + wf_aligner->align_status.num_null_steps = 0; + // Set limits + int hi, lo; + wavefront_compute_limits_input(wf_aligner,&wavefront_set,&lo,&hi); + // Allocate wavefronts + wavefront_compute_allocate_output(wf_aligner,&wavefront_set,score,lo,hi); + // Init wavefront ends + wavefront_compute_init_ends(wf_aligner,&wavefront_set,lo,hi); + // Compute Wavefronts + wavefront_compute_linear_dispatcher(wf_aligner,&wavefront_set,lo,hi); + // Offload backtrace (if necessary) + if (wf_aligner->wf_components.bt_piggyback) { + wavefront_backtrace_offload_linear(wf_aligner,&wavefront_set,lo,hi); + } + // Process wavefront ends + wavefront_compute_process_ends(wf_aligner,&wavefront_set,score); +} + + diff --git a/src/lib/wfa2/wavefront/wavefront_compute_linear.h b/src/lib/wfa2/wavefront/wavefront_compute_linear.h new file mode 100644 index 000000000..eddec6f32 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_compute_linear.h @@ -0,0 +1,44 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront alignment module for computing wavefronts (gap-linear) + */ + +#ifndef WAVEFRONT_COMPUTE_LINEAR_H_ +#define WAVEFRONT_COMPUTE_LINEAR_H_ + +#include "wavefront_aligner.h" + +/* + * Compute wavefront (gap-linear) + */ +void wavefront_compute_linear( + wavefront_aligner_t* const wf_aligner, + const int score); + +#endif /* WAVEFRONT_COMPUTE_LINEAR_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_debug.c b/src/lib/wfa2/wavefront/wavefront_debug.c new file mode 100644 index 000000000..f309576c3 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_debug.c @@ -0,0 +1,262 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront-Alignment module for debugging and collect stats + */ + +#include "../utils/commons.h" +#include "wavefront_debug.h" +#include "wavefront_align.h" +#include "wavefront_compute.h" + +/* + * Checks + */ +bool wavefront_check_alignment( + FILE* const stream, + wavefront_aligner_t* const wf_aligner) { + // Parameters + const char* const pattern = wf_aligner->pattern; + const int pattern_length = wf_aligner->pattern_length; + const char* const text = wf_aligner->text; + const int text_length = wf_aligner->text_length; + // Custom function to compare sequences + alignment_match_funct_t match_funct = wf_aligner->match_funct; + void* match_funct_arguments = wf_aligner->match_funct_arguments; + // CIGAR + cigar_t* const cigar = wf_aligner->cigar; + char* const operations = cigar->operations; + const int begin_offset = cigar->begin_offset; + const int end_offset = cigar->end_offset; + // Traverse CIGAR + bool alignment_correct = true; + int pattern_pos=0, text_pos=0, i; + for (i=begin_offset;ipattern; + const int pattern_length = wf_aligner->pattern_length; + const char* const text = wf_aligner->text; + const int text_length = wf_aligner->text_length; + const int status = wf_aligner->align_status.status; + const uint64_t memory_used = wf_aligner->align_status.memory_used; + // Banner + fprintf(stream,"[WFA::Debug]"); + // Sequences + const int score = wavefront_compute_classic_score( + wf_aligner,wf_aligner->pattern_length, + wf_aligner->text_length,wf_aligner->cigar->score); + fprintf(stream,"\t%d",score); + fprintf(stream,"\t%d\t%d",pattern_length,text_length); + fprintf(stream,"\t%s",(status==0) ? "OK" : "FAIL"); + fprintf(stream,"\t%2.3f",TIMER_GET_TOTAL_MS(&wf_aligner->system.timer)); + fprintf(stream,"\t%luMB\t",CONVERT_B_TO_MB(memory_used)); + fprintf(stream,"["); + wavefront_aligner_print_type(stream,wf_aligner); + fprintf(stream,","); + wavefront_aligner_print_scope(stream,wf_aligner); + fprintf(stream,","); + wavefront_penalties_print(stream,&wf_aligner->penalties); + fprintf(stream,"]\t"); + cigar_print(stream,wf_aligner->cigar,true); + if (wf_aligner->match_funct != NULL) { + fprintf(stream,"\t-\t-"); + } else { + fprintf(stream,"\t%.*s\t%.*s",pattern_length,pattern,text_length,text); + } + fprintf(stream,"\n"); +} +void wavefront_report_verbose_begin( + FILE* const stream, + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length) { + // Input sequences + fprintf(stream,"[WFA::Report::Begin] ["); + wavefront_aligner_print_type(stream,wf_aligner); + fprintf(stream,"]-Alignment (obj=%p)\n",wf_aligner); + if (wf_aligner->match_funct != NULL) { + fprintf(stream,"[WFA::Report]\tPattern\t%d\tcustom-funct()\n",pattern_length); + fprintf(stream,"[WFA::Report]\tText\t%d\tcustom-funct()\n",text_length); + } else { + fprintf(stream,"[WFA::Report]\tPattern\t%d\t%.*s\n",pattern_length,pattern_length,pattern); + fprintf(stream,"[WFA::Report]\tText\t%d\t%.*s\n",text_length,text_length,text); + } + // Alignment scope/form + fprintf(stream,"[WFA::Report]\tScope="); + wavefront_aligner_print_scope(stream,wf_aligner); + fprintf(stream," Max-score=%d", + wf_aligner->system.max_alignment_score); + // Penalties + fprintf(stream," Penalties="); + wavefront_penalties_print(stream,&wf_aligner->penalties); + // Heuristic + fprintf(stream," Heuristic="); + wavefront_heuristic_print(stream,&wf_aligner->heuristic); + // Memory mode + fprintf(stream," Memory.mode=(%d,%luMB,%luMB,%luMB)\n", + wf_aligner->memory_mode, + CONVERT_B_TO_MB(wf_aligner->system.max_memory_compact), + CONVERT_B_TO_MB(wf_aligner->system.max_memory_resident), + CONVERT_B_TO_MB(wf_aligner->system.max_memory_abort)); +} +void wavefront_report_verbose_end( + FILE* const stream, + wavefront_aligner_t* const wf_aligner) { + // Finish report + fprintf(stream,"[WFA::Report::End]\tFinish.status=%d",wf_aligner->align_status.status); + fprintf(stream," Time.taken="); + timer_print_total(stream,&wf_aligner->system.timer); + fprintf(stream," Memory.used=%luMB", + CONVERT_B_TO_MB(wf_aligner->align_status.memory_used)); + fprintf(stream," WFA.components=(wfs=%d,maxlo=%d,maxhi=%d)", + wf_aligner->wf_components.num_wavefronts, + wf_aligner->wf_components.historic_min_lo, + wf_aligner->wf_components.historic_max_hi); + const int score = wavefront_compute_classic_score( + wf_aligner,wf_aligner->pattern_length, + wf_aligner->text_length,wf_aligner->cigar->score); + fprintf(stream," WFA.score=%d",score); + fprintf(stream," WFA.cigar="); + cigar_print(stream,wf_aligner->cigar,true); + fprintf(stream,"\n"); +} +/* + * Debug + */ +void wavefront_debug_prologue( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length) { + // Check verbose level + if (wf_aligner->system.verbose >= 1) { + timer_start(&wf_aligner->system.timer); + if (wf_aligner->system.verbose >= 4) { + wavefront_report_verbose_begin(stderr,wf_aligner, + pattern,pattern_length,text,text_length); + } + } +} +void wavefront_debug_epilogue( + wavefront_aligner_t* const wf_aligner) { + // Print Summary + if (wf_aligner->system.verbose >= 1) { + timer_stop(&wf_aligner->system.timer); + if (wf_aligner->system.verbose >= 4) { + wavefront_report_verbose_end(stderr,wf_aligner); + } + wavefront_report_lite(stderr,wf_aligner); + } +} +/* + * Check + */ +void wavefront_debug_check_correct( + wavefront_aligner_t* const wf_aligner) { + // Check correct + if (wf_aligner->system.check_alignment_correct && + wf_aligner->align_status.status == WF_STATUS_SUCCESSFUL && + wf_aligner->alignment_scope == compute_alignment) { + if (!wavefront_check_alignment(stderr,wf_aligner)) { + fprintf(stderr,"[WFA::Check] Error: Alignment incorrect\n"); + exit(1); + } + } +} + + + + diff --git a/src/lib/wfa2/wavefront/wavefront_debug.h b/src/lib/wfa2/wavefront/wavefront_debug.h new file mode 100644 index 000000000..1bdb85320 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_debug.h @@ -0,0 +1,55 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront-Alignment module for debugging and collect stats + */ + +#ifndef WAVEFRONT_DEBUG_H_ +#define WAVEFRONT_DEBUG_H_ + +#include "wavefront_aligner.h" + +/* + * Debug + */ +void wavefront_debug_prologue( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length); +void wavefront_debug_epilogue( + wavefront_aligner_t* const wf_aligner); + +/* + * Check + */ +void wavefront_debug_check_correct( + wavefront_aligner_t* const wf_aligner); + +#endif /* WAVEFRONT_DEBUG_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_display.c b/src/lib/wfa2/wavefront/wavefront_display.c new file mode 100644 index 000000000..e7bc747ee --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_display.c @@ -0,0 +1,282 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront-Alignment module for display and report + */ + +#include "wavefront_display.h" +#include "wavefront_aligner.h" +#include "wavefront_compute.h" + +/* + * Constants + */ +#define WF_DISPLAY_YLABEL_LENGTH 8 + +/* + * Compute dimensions + */ +int wavefront_display_compute_row_width( + const distance_metric_t distance_metric, + const int bt_length) { + // Compute number of components + int num_components = 1; + switch (distance_metric) { + case indel: + case edit: + case gap_linear: num_components = 1; break; + case gap_affine: num_components = 3; break; + case gap_affine_2p: num_components = 5; break; + default: break; + } + // Return + return ((num_components)*(4+(bt_length))); +} +void wavefront_display_compute_limits( + wavefront_aligner_t* const wf_aligner, + const int score_begin, + const int score_end, + int* const out_max_k, + int* const out_min_k) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + // Compute min/max k + int i, max_k=INT_MIN, min_k=INT_MAX; + for (i=score_begin;i<=score_end;++i) { + const int s = (wf_components->memory_modular) ? i%wf_components->max_score_scope : i; + wavefront_t* const mwavefront = wf_components->mwavefronts[s]; + if (mwavefront != NULL) { + max_k = MAX(max_k,mwavefront->hi); + min_k = MIN(min_k,mwavefront->lo); + } + if (distance_metric <= gap_linear) continue; + wavefront_t* const i1wavefront = wf_components->i1wavefronts[s]; + if (i1wavefront != NULL) { + max_k = MAX(max_k,i1wavefront->hi); + min_k = MIN(min_k,i1wavefront->lo); + } + wavefront_t* const d1wavefront = wf_components->d1wavefronts[s]; + if (d1wavefront != NULL) { + max_k = MAX(max_k,d1wavefront->hi); + min_k = MIN(min_k,d1wavefront->lo); + } + if (distance_metric == gap_affine) continue; + wavefront_t* const i2wavefront = wf_components->i2wavefronts[s]; + if (i2wavefront != NULL) { + max_k = MAX(max_k,i2wavefront->hi); + min_k = MIN(min_k,i2wavefront->lo); + } + wavefront_t* const d2wavefront = wf_components->d2wavefronts[s]; + if (d2wavefront != NULL) { + max_k = MAX(max_k,d2wavefront->hi); + min_k = MIN(min_k,d2wavefront->lo); + } + } + // Return + *out_max_k = max_k; + *out_min_k = min_k; +} +/* + * Display components + */ +void wavefront_display_print_element( + FILE* const stream, + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront, + const int k, + const int bt_length) { + // Check limits + if (wavefront!=NULL && wavefront->lo <= k && k <= wavefront->hi) { + const wf_offset_t offset = wavefront->offsets[k]; + if (offset >= 0) { + fprintf(stream,"[%2d]",(int)offset); + // Print pcigar + if (bt_length > 0) { + char cigar_buffer[32]; + const int pcigar_length = pcigar_unpack(wavefront->bt_pcigar[k],cigar_buffer); + const int effective_pcigar_length = MIN(bt_length,pcigar_length); + fprintf(stream,"%.*s",effective_pcigar_length,cigar_buffer); + PRINT_CHAR_REP(stream,' ',bt_length-effective_pcigar_length); + } + } else { + fprintf(stream,"[ ]"); + PRINT_CHAR_REP(stream,' ',bt_length); + } + } else { + PRINT_CHAR_REP(stream,' ',bt_length+4); + } +} +void wavefront_display_print_frame( + FILE* const stream, + const int score_begin, + const int score_end, + const int row_width, + const int bt_length) { + // Align Y-label [k=000] + PRINT_CHAR_REP(stream,' ',WF_DISPLAY_YLABEL_LENGTH); + // Frame + fprintf(stream,"+"); + const int num_blocks = score_end-score_begin+1; + const int frame_length = (num_blocks*row_width) + (num_blocks-1); + PRINT_CHAR_REP(stream,'-',frame_length); + fprintf(stream,"+\n"); +} +void wavefront_display_print_score( + FILE* const stream, + const int score_begin, + const int score_end, + const int row_width, + const int bt_length) { + // Score label + PRINT_CHAR_REP(stream,' ',WF_DISPLAY_YLABEL_LENGTH); + int s; + for (s=score_begin;s<=score_end;++s) { + fprintf(stream,"|"); + if (row_width >= 10) { + PRINT_CHAR_REP(stream,' ',row_width-10); + fprintf(stream,"%4d-score",s); + } else { + fprintf(stream,"s=%2d",s); + } + } + fprintf(stream,"|\n"); +} +void wavefront_display_print_header_component( + FILE* const stream, + wavefront_t* const wavefront, + char* const wavefront_id, + const int bt_length) { + fprintf(stream,"[%s]",wavefront_id); + if (bt_length > 0) { + if (wavefront!=NULL && bt_length >= 10) { + PRINT_CHAR_REP(stream,' ',bt_length-10); + fprintf(stream,"[|BT|=%2d]",wavefront->bt_occupancy_max); + } else { + PRINT_CHAR_REP(stream,' ',bt_length-1); + } + } +} +void wavefront_display_print_header( + FILE* const stream, + wavefront_aligner_t* const wf_aligner, + const int score_begin, + const int score_end, + const int bt_length) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + const int row_width = wavefront_display_compute_row_width(distance_metric,bt_length); + // Score header + fprintf(stream,"\n>[SCORE %d-%d]\n",score_begin,score_end); + // Frame + wavefront_display_print_frame(stream,score_begin,score_end,row_width,bt_length); + // Score header + wavefront_display_print_score(stream,score_begin,score_end,row_width,bt_length); + // Frame + wavefront_display_print_frame(stream,score_begin,score_end,row_width,bt_length); + // Wavefront labels + PRINT_CHAR_REP(stream,' ',WF_DISPLAY_YLABEL_LENGTH); // Align [k= ] + int score; + for (score=score_begin;score<=score_end;++score) { + const int s = (wf_components->memory_modular) ? score%wf_components->max_score_scope : score; + fprintf(stream,"|"); + wavefront_display_print_header_component(stream,wf_components->mwavefronts[s]," M",bt_length); + if (distance_metric <= gap_linear) continue; + wavefront_display_print_header_component(stream,wf_components->i1wavefronts[s],"I1",bt_length); + wavefront_display_print_header_component(stream,wf_components->d1wavefronts[s],"D1",bt_length); + if (distance_metric == gap_affine) continue; + wavefront_display_print_header_component(stream,wf_components->i2wavefronts[s],"I2",bt_length); + wavefront_display_print_header_component(stream,wf_components->d2wavefronts[s],"D2",bt_length); + } + fprintf(stream,"|\n"); + // Frame + wavefront_display_print_frame(stream,score_begin,score_end,row_width,bt_length); +} +/* + * Display + */ +void wavefront_aligner_print_block( + FILE* const stream, + wavefront_aligner_t* const wf_aligner, + const int score_begin, + const int score_end, + int bt_length) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + if (!wf_components->bt_piggyback) bt_length = 0; // Check BT + // Compute dinmensions + int max_k, min_k; + wavefront_display_compute_limits(wf_aligner,score_begin,score_end,&max_k,&min_k); + // Header + wavefront_display_print_header(stream,wf_aligner,score_begin,score_end,bt_length); + // Traverse all diagonals + int k; + for (k=max_k;k>=min_k;k--) { + fprintf(stream,"[k=%3d] ",k); + // Traverse all scores + int i; + for (i=score_begin;i<=score_end;++i) { + const int s = (wf_components->memory_modular) ? i%wf_components->max_score_scope : i; + fprintf(stream,"|"); + // Fetch wavefront + wavefront_t* const mwavefront = wf_components->mwavefronts[s]; + wavefront_display_print_element(stream,wf_aligner,mwavefront,k,bt_length); + if (distance_metric <= gap_linear) continue; + wavefront_t* const i1wavefront = wf_components->i1wavefronts[s]; + wavefront_t* const d1wavefront = wf_components->d1wavefronts[s]; + wavefront_display_print_element(stream,wf_aligner,i1wavefront,k,bt_length); + wavefront_display_print_element(stream,wf_aligner,d1wavefront,k,bt_length); + if (distance_metric == gap_affine) continue; + wavefront_t* const i2wavefront = wf_components->i2wavefronts[s]; + wavefront_t* const d2wavefront = wf_components->d2wavefronts[s]; + wavefront_display_print_element(stream,wf_aligner,i2wavefront,k,bt_length); + wavefront_display_print_element(stream,wf_aligner,d2wavefront,k,bt_length); + } + fprintf(stream,"|\n"); + } + // Footer + const int row_width = wavefront_display_compute_row_width(distance_metric,bt_length); + wavefront_display_print_frame(stream,score_begin,score_end,row_width,bt_length); +} +void wavefront_aligner_print( + FILE* const stream, + wavefront_aligner_t* const wf_aligner, + const int score_begin, + const int score_end, + const int num_wfs_per_row, + const int backtrace_length) { + // Print wavefronts by chunks + int s; + for (s=MAX(score_begin,0);s<=score_end;s+=num_wfs_per_row-1) { + const int block_score_end = MIN(s+num_wfs_per_row-1,score_end); + wavefront_aligner_print_block(stream,wf_aligner,s,block_score_end,backtrace_length); + if (block_score_end == score_end) break; + } +} diff --git a/src/lib/wfa2/wavefront/wavefront_display.h b/src/lib/wfa2/wavefront/wavefront_display.h new file mode 100644 index 000000000..9b94b47a3 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_display.h @@ -0,0 +1,51 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront-Alignment module for display and report + */ + +#ifndef WAVEFRONT_DISPLAY_H_ +#define WAVEFRONT_DISPLAY_H_ + +#include "../utils/commons.h" + +// Wavefront ahead definition +typedef struct _wavefront_aligner_t wavefront_aligner_t; + +/* + * Display + */ +void wavefront_aligner_print( + FILE* const stream, + wavefront_aligner_t* const wf_aligner, + const int score_begin, + const int score_end, + const int num_wfs_per_row, + const int backtrace_length); + +#endif /* WAVEFRONT_DISPLAY_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_extend.c b/src/lib/wfa2/wavefront/wavefront_extend.c new file mode 100644 index 000000000..74cd33b9d --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_extend.c @@ -0,0 +1,516 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront-Alignment module for the "extension" of exact matches + */ + +#include "../utils/string_padded.h" +#include "wavefront_extend.h" +#include "wavefront_align.h" +#include "wavefront_compute.h" +#include "wavefront_heuristic.h" + +#ifdef WFA_PARALLEL +#include +#endif + +/* + * Termination (detect end of alignment) + */ +bool wavefront_extend_end2end_check_termination( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const mwavefront, + const int score, + const int score_mod) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + const affine2p_matrix_type component_end = wf_aligner->component_end; + const int alignment_k = DPMATRIX_DIAGONAL(text_length,pattern_length); + const wf_offset_t alignment_offset = DPMATRIX_OFFSET(text_length,pattern_length); + // Select end component + switch (component_end) { + case affine2p_matrix_M: { + // Check diagonal/offset + if (mwavefront->lo > alignment_k || alignment_k > mwavefront->hi) return false; // Not done + const wf_offset_t moffset = mwavefront->offsets[alignment_k]; + if (moffset < alignment_offset) return false; // Not done + // We are done + wf_aligner->alignment_end_pos.score = score; + wf_aligner->alignment_end_pos.k = alignment_k; + wf_aligner->alignment_end_pos.offset = alignment_offset; + return true; + } + case affine2p_matrix_I1: { + // Fetch I1-wavefront & check diagonal/offset + wavefront_t* const i1wavefront = wf_aligner->wf_components.i1wavefronts[score_mod]; + if (i1wavefront == NULL || i1wavefront->lo > alignment_k || alignment_k > i1wavefront->hi) return false; // Not done + const wf_offset_t i1offset = i1wavefront->offsets[alignment_k]; + if (i1offset < alignment_offset) return false; // Not done + // We are done + wf_aligner->alignment_end_pos.score = score; + wf_aligner->alignment_end_pos.k = alignment_k; + wf_aligner->alignment_end_pos.offset = alignment_offset; + return true; + } + case affine2p_matrix_I2: { + // Fetch I2-wavefront & check diagonal/offset + wavefront_t* const i2wavefront = wf_aligner->wf_components.i2wavefronts[score_mod]; + if (i2wavefront == NULL || i2wavefront->lo > alignment_k || alignment_k > i2wavefront->hi) return false; // Not done + const wf_offset_t i2offset = i2wavefront->offsets[alignment_k]; + if (i2offset < alignment_offset) return false; // Not done + // We are done + wf_aligner->alignment_end_pos.score = score; + wf_aligner->alignment_end_pos.k = alignment_k; + wf_aligner->alignment_end_pos.offset = alignment_offset; + return true; + } + case affine2p_matrix_D1: { + // Fetch D1-wavefront & check diagonal/offset + wavefront_t* const d1wavefront = wf_aligner->wf_components.d1wavefronts[score_mod]; + if (d1wavefront == NULL || d1wavefront->lo > alignment_k || alignment_k > d1wavefront->hi) return false; // Not done + const wf_offset_t d1offset = d1wavefront->offsets[alignment_k]; + if (d1offset < alignment_offset) return false; // Not done + // We are done + wf_aligner->alignment_end_pos.score = score; + wf_aligner->alignment_end_pos.k = alignment_k; + wf_aligner->alignment_end_pos.offset = alignment_offset; + return true; + } + case affine2p_matrix_D2: { + // Fetch D2-wavefront & check diagonal/offset + wavefront_t* const d2wavefront = wf_aligner->wf_components.d2wavefronts[score_mod]; + if (d2wavefront == NULL || d2wavefront->lo > alignment_k || alignment_k > d2wavefront->hi) return false; // Not done + const wf_offset_t d2offset = d2wavefront->offsets[alignment_k]; + if (d2offset < alignment_offset) return false; // Not done + // We are done + wf_aligner->alignment_end_pos.score = score; + wf_aligner->alignment_end_pos.k = alignment_k; + wf_aligner->alignment_end_pos.offset = alignment_offset; + return true; + } + default: + break; + } + return false; +} +bool wavefront_extend_endsfree_check_termination( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const mwavefront, + const int score, + const int k, + const wf_offset_t offset) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + // Check ends-free reaching boundaries + const int h_pos = WAVEFRONT_H(k,offset); + const int v_pos = WAVEFRONT_V(k,offset); + if (h_pos >= text_length) { // Text is aligned + // Is Pattern end-free? + const int pattern_left = pattern_length - v_pos; + const int pattern_end_free = wf_aligner->alignment_form.pattern_end_free; + if (pattern_left <= pattern_end_free) { + #ifdef WFA_PARALLEL + #pragma omp critical + #endif + { + wf_aligner->alignment_end_pos.score = score; + wf_aligner->alignment_end_pos.k = k; + wf_aligner->alignment_end_pos.offset = offset; + } + return true; // Quit (we are done) + } + } + if (v_pos >= pattern_length) { // Pattern is aligned + // Is text end-free? + const int text_left = text_length - h_pos; + const int text_end_free = wf_aligner->alignment_form.text_end_free; + if (text_left <= text_end_free) { + #ifdef WFA_PARALLEL + #pragma omp critical + #endif + { + wf_aligner->alignment_end_pos.score = score; + wf_aligner->alignment_end_pos.k = k; + wf_aligner->alignment_end_pos.offset = offset; + } + return true; // Quit (we are done) + } + } + // Not done + return false; +} +/* + * Extend kernel + */ +FORCE_INLINE wf_offset_t wavefront_extend_matches_packed_kernel( + wavefront_aligner_t* const wf_aligner, + const int k, + wf_offset_t offset) { + // Fetch pattern/text blocks + uint64_t* pattern_blocks = (uint64_t*)(wf_aligner->pattern+WAVEFRONT_V(k,offset)); + uint64_t* text_blocks = (uint64_t*)(wf_aligner->text+WAVEFRONT_H(k,offset)); + // Compare 64-bits blocks + uint64_t cmp = *pattern_blocks ^ *text_blocks; + while (__builtin_expect(cmp==0,0)) { + // Increment offset (full block) + offset += 8; + // Next blocks + ++pattern_blocks; + ++text_blocks; + // Compare + cmp = *pattern_blocks ^ *text_blocks; + } + // Count equal characters + const int equal_right_bits = __builtin_ctzl(cmp); + const int equal_chars = DIV_FLOOR(equal_right_bits,8); + offset += equal_chars; + // Return extended offset + return offset; +} +/* + * Wavefront offset extension comparing characters + * Remember: + * - No offset is out of boundaries !(h>tlen,v>plen) + * - if (h==tlen,v==plen) extension won't increment (sentinels) + */ +FORCE_NO_INLINE void wavefront_extend_matches_packed_end2end( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const mwavefront, + const int lo, + const int hi) { + wf_offset_t* const offsets = mwavefront->offsets; + int k; + for (k=lo;k<=hi;++k) { + // Fetch offset + const wf_offset_t offset = offsets[k]; + if (offset == WAVEFRONT_OFFSET_NULL) continue; + // Extend offset + offsets[k] = wavefront_extend_matches_packed_kernel(wf_aligner,k,offset); + } +} +FORCE_NO_INLINE wf_offset_t wavefront_extend_matches_packed_max( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const mwavefront, + const int lo, + const int hi) { + wf_offset_t* const offsets = mwavefront->offsets; + wf_offset_t max_antidiag = 0; + int k; + for (k=lo;k<=hi;++k) { + // Fetch offset + const wf_offset_t offset = offsets[k]; + if (offset == WAVEFRONT_OFFSET_NULL) continue; + // Extend offset + offsets[k] = wavefront_extend_matches_packed_kernel(wf_aligner,k,offset); + // Compute max + const wf_offset_t antidiag = WAVEFRONT_ANTIDIAGONAL(k,offsets[k]); + if (max_antidiag < antidiag) max_antidiag = antidiag; + } + return max_antidiag; +} +FORCE_NO_INLINE bool wavefront_extend_matches_packed_endsfree( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const mwavefront, + const int score, + const int lo, + const int hi) { + wf_offset_t* const offsets = mwavefront->offsets; + int k; + for (k=lo;k<=hi;++k) { + // Fetch offset + wf_offset_t offset = offsets[k]; + if (offset == WAVEFRONT_OFFSET_NULL) continue; + // Extend offset + offset = wavefront_extend_matches_packed_kernel(wf_aligner,k,offset); + offsets[k] = offset; + // Check ends-free reaching boundaries + if (wavefront_extend_endsfree_check_termination(wf_aligner,mwavefront,score,k,offset)) { + return true; // Quit (we are done) + } + } + // Alignment not finished + return false; +} +bool wavefront_extend_matches_custom( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const mwavefront, + const int score, + const int lo, + const int hi, + const bool endsfree) { + // Parameters (custom matching function) + alignment_match_funct_t match_funct = wf_aligner->match_funct; + void* const func_arguments = wf_aligner->match_funct_arguments; + // Extend diagonally each wavefront point + wf_offset_t* const offsets = mwavefront->offsets; + int k; + for (k=lo;k<=hi;++k) { + // Check offset + wf_offset_t offset = offsets[k]; + if (offset == WAVEFRONT_OFFSET_NULL) continue; + // Count equal characters + int v = WAVEFRONT_V(k,offset); + int h = WAVEFRONT_H(k,offset); + while (match_funct(v,h,func_arguments)) { + h++; v++; offset++; + } + // Update offset + offsets[k] = offset; + // Check ends-free reaching boundaries + if (endsfree && wavefront_extend_endsfree_check_termination(wf_aligner,mwavefront,score,k,offset)) { + return true; // Quit (we are done) + } + } + // Alignment not finished + return false; +} +/* + * Wavefront exact "extension" + */ +int wavefront_extend_end2end_max( + wavefront_aligner_t* const wf_aligner, + const int score, + int* const max_antidiagonal) { + // Compute score + const bool memory_modular = wf_aligner->wf_components.memory_modular; + const int max_score_scope = wf_aligner->wf_components.max_score_scope; + const int score_mod = (memory_modular) ? score % max_score_scope : score; + *max_antidiagonal = 0; // Init + // Fetch m-wavefront + wavefront_t* const mwavefront = wf_aligner->wf_components.mwavefronts[score_mod]; + if (mwavefront == NULL) { + // Check alignment feasibility (for heuristic variants that can lead to no solution) + if (wf_aligner->align_status.num_null_steps > wf_aligner->wf_components.max_score_scope) { + wf_aligner->align_status.status = WF_STATUS_UNFEASIBLE; + wf_aligner->align_status.score = score; + return 1; // Done + } + return 0; // Not done + } + // Multithreading dispatcher + const int lo = mwavefront->lo; + const int hi = mwavefront->hi; + wf_offset_t max_antidiag = 0; + const int num_threads = wavefront_compute_num_threads(wf_aligner,lo,hi); + if (num_threads == 1) { + // Extend wavefront + max_antidiag = wavefront_extend_matches_packed_max(wf_aligner,mwavefront,lo,hi); + } else { +#ifdef WFA_PARALLEL + // Extend wavefront in parallel + #pragma omp parallel num_threads(num_threads) + { + int t_lo, t_hi; + wavefront_compute_thread_limits( + omp_get_thread_num(),omp_get_num_threads(),lo,hi,&t_lo,&t_hi); + wf_offset_t t_max_antidiag = wavefront_extend_matches_packed_max(wf_aligner,mwavefront,t_lo,t_hi); + #ifdef WFA_PARALLEL + #pragma omp critical + #endif + { + if (t_max_antidiag > max_antidiag) max_antidiag = t_max_antidiag; + } + } +#endif + } + // Check end-to-end finished + const bool end_reached = wavefront_extend_end2end_check_termination(wf_aligner,mwavefront,score,score_mod); + if (end_reached) { + wf_aligner->align_status.status = WF_STATUS_END_REACHED; + wf_aligner->align_status.score = score; + return 1; // Done + } + // Cut-off wavefront heuristically + if (wf_aligner->heuristic.strategy != wf_heuristic_none) { + wavefront_heuristic_cufoff(wf_aligner,score,score_mod); + } + *max_antidiagonal = max_antidiag; + return 0; // Not done +} +int wavefront_extend_end2end( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Compute score + const bool memory_modular = wf_aligner->wf_components.memory_modular; + const int max_score_scope = wf_aligner->wf_components.max_score_scope; + const int score_mod = (memory_modular) ? score % max_score_scope : score; + // Fetch m-wavefront + wavefront_t* const mwavefront = wf_aligner->wf_components.mwavefronts[score_mod]; + if (mwavefront == NULL) { + // Check alignment feasibility (for heuristic variants that can lead to no solution) + if (wf_aligner->align_status.num_null_steps > wf_aligner->wf_components.max_score_scope) { + wf_aligner->align_status.status = WF_STATUS_UNFEASIBLE; + wf_aligner->align_status.score = score; + return 1; // Done + } + return 0; // Not done + } + // Multithreading dispatcher + const int lo = mwavefront->lo; + const int hi = mwavefront->hi; + bool end_reached = false; + const int num_threads = wavefront_compute_num_threads(wf_aligner,lo,hi); + if (num_threads == 1) { + // Extend wavefront + wavefront_extend_matches_packed_end2end(wf_aligner,mwavefront,lo,hi); + } else { +#ifdef WFA_PARALLEL + // Extend wavefront in parallel + #pragma omp parallel num_threads(num_threads) + { + int t_lo, t_hi; + wavefront_compute_thread_limits( + omp_get_thread_num(),omp_get_num_threads(),lo,hi,&t_lo,&t_hi); + wavefront_extend_matches_packed_end2end(wf_aligner,mwavefront,t_lo,t_hi); + } +#endif + } + // Check end-to-end finished + end_reached = wavefront_extend_end2end_check_termination(wf_aligner,mwavefront,score,score_mod); + if (end_reached) { + wf_aligner->align_status.status = WF_STATUS_END_REACHED; + wf_aligner->align_status.score = score; + return 1; // Done + } + // Cut-off wavefront heuristically + if (wf_aligner->heuristic.strategy != wf_heuristic_none) { + wavefront_heuristic_cufoff(wf_aligner,score,score_mod); + } + return 0; // Not done +} +int wavefront_extend_endsfree( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Modular wavefront + const bool memory_modular = wf_aligner->wf_components.memory_modular; + const int max_score_scope = wf_aligner->wf_components.max_score_scope; + const int score_mod = (memory_modular) ? score % max_score_scope : score; + // Fetch m-wavefront + wavefront_t* const mwavefront = wf_aligner->wf_components.mwavefronts[score_mod]; + if (mwavefront == NULL) { + // Check alignment feasibility (for heuristic variants that can lead to no solution) + if (wf_aligner->align_status.num_null_steps > wf_aligner->wf_components.max_score_scope) { + wf_aligner->align_status.status = WF_STATUS_UNFEASIBLE; + wf_aligner->align_status.score = score; + return 1; // Done + } + return 0; // Not done + } + // Multithreading dispatcher + const int lo = mwavefront->lo; + const int hi = mwavefront->hi; + bool end_reached = false; + const int num_threads = wavefront_compute_num_threads(wf_aligner,lo,hi); + if (num_threads == 1) { + // Extend wavefront + end_reached = wavefront_extend_matches_packed_endsfree(wf_aligner,mwavefront,score,lo,hi); + } else { +#ifdef WFA_PARALLEL + // Extend wavefront in parallel + #pragma omp parallel num_threads(num_threads) + { + int t_lo, t_hi; + wavefront_compute_thread_limits( + omp_get_thread_num(),omp_get_num_threads(),lo,hi,&t_lo,&t_hi); + if (wavefront_extend_matches_packed_endsfree(wf_aligner,mwavefront,score,t_lo,t_hi)) { + end_reached = true; + } + } +#endif + } + if (end_reached) { + wf_aligner->align_status.status = WF_STATUS_END_REACHED; + wf_aligner->align_status.score = score; + return 1; // Done + } + // Cut-off wavefront heuristically + if (wf_aligner->heuristic.strategy != wf_heuristic_none) { + wavefront_heuristic_cufoff(wf_aligner,score,score_mod); + } + return 0; // Not done +} +int wavefront_extend_custom( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Compute score + const bool memory_modular = wf_aligner->wf_components.memory_modular; + const int max_score_scope = wf_aligner->wf_components.max_score_scope; + const int score_mod = (memory_modular) ? score % max_score_scope : score; + // Fetch m-wavefront + wavefront_t* const mwavefront = wf_aligner->wf_components.mwavefronts[score_mod]; + if (mwavefront == NULL) { + // Check alignment feasibility (for heuristic variants that can lead to no solution) + if (wf_aligner->align_status.num_null_steps > wf_aligner->wf_components.max_score_scope) { + wf_aligner->align_status.status = WF_STATUS_UNFEASIBLE; + wf_aligner->align_status.score = score; + return 1; // Done + } + return 0; // Not done + } + // Multithreading dispatcher + const bool endsfree = (wf_aligner->alignment_form.span == alignment_endsfree); + const int lo = mwavefront->lo; + const int hi = mwavefront->hi; + bool end_reached = false; + const int num_threads = wavefront_compute_num_threads(wf_aligner,lo,hi); + if (num_threads == 1) { + // Extend wavefront + end_reached = wavefront_extend_matches_custom(wf_aligner,mwavefront,score,lo,hi,endsfree); + } else { +#ifdef WFA_PARALLEL + // Extend wavefront in parallel + #pragma omp parallel num_threads(num_threads) + { + int t_lo, t_hi; + wavefront_compute_thread_limits( + omp_get_thread_num(),omp_get_num_threads(),lo,hi,&t_lo,&t_hi); + if (wavefront_extend_matches_custom(wf_aligner,mwavefront,score,t_lo,t_hi,endsfree)) { + end_reached = true; + } + } +#endif + } + // Check end-to-end finished + if (!endsfree) { + end_reached = wavefront_extend_end2end_check_termination(wf_aligner,mwavefront,score,score_mod); + } + if (end_reached) { + wf_aligner->align_status.status = WF_STATUS_END_REACHED; + wf_aligner->align_status.score = score; + return 1; // Done + } + // Cut-off wavefront heuristically + if (wf_aligner->heuristic.strategy != wf_heuristic_none) { + wavefront_heuristic_cufoff(wf_aligner,score,score_mod); + } + return 0; // Not done +} + + diff --git a/src/lib/wfa2/wavefront/wavefront_extend.h b/src/lib/wfa2/wavefront/wavefront_extend.h new file mode 100644 index 000000000..934558967 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_extend.h @@ -0,0 +1,54 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront-Alignment module for the "extension" of exact matches + */ + +#ifndef WAVEFRONT_EXTEND_H_ +#define WAVEFRONT_EXTEND_H_ + +#include "wavefront_aligner.h" + +/* + * Wavefront exact "extension" + */ +int wavefront_extend_end2end( + wavefront_aligner_t* const wf_aligner, + const int score); +int wavefront_extend_end2end_max( + wavefront_aligner_t* const wf_aligner, + const int score, + int* const max_antidiagonal); +int wavefront_extend_endsfree( + wavefront_aligner_t* const wf_aligner, + const int score); +int wavefront_extend_custom( + wavefront_aligner_t* const wf_aligner, + const int score); + +#endif /* WAVEFRONT_EXTEND_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_heuristic.c b/src/lib/wfa2/wavefront/wavefront_heuristic.c new file mode 100644 index 000000000..0eb6409eb --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_heuristic.c @@ -0,0 +1,606 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Support functions for wavefront heuristic strategies + */ + +#include "wavefront_heuristic.h" +#include "wavefront_aligner.h" + +/* + * Setup + */ +void wavefront_heuristic_set_none( + wavefront_heuristic_t* const wf_heuristic) { + wf_heuristic->strategy = wf_heuristic_none; +} +void wavefront_heuristic_set_wfadaptive( + wavefront_heuristic_t* const wf_heuristic, + const int min_wavefront_length, + const int max_distance_threshold, + const int steps_between_cutoffs) { + wf_heuristic->strategy |= wf_heuristic_wfadaptive; + wf_heuristic->min_wavefront_length = min_wavefront_length; + wf_heuristic->max_distance_threshold = max_distance_threshold; + wf_heuristic->steps_between_cutoffs = steps_between_cutoffs; + // Internals + wf_heuristic->steps_wait = steps_between_cutoffs; +} +void wavefront_heuristic_set_wfmash( + wavefront_heuristic_t* const wf_heuristic, + const int min_wavefront_length, + const int max_distance_threshold, + const int steps_between_cutoffs) { + wf_heuristic->strategy |= wf_heuristic_wfmash; + wf_heuristic->min_wavefront_length = min_wavefront_length; + wf_heuristic->max_distance_threshold = max_distance_threshold; + wf_heuristic->steps_between_cutoffs = steps_between_cutoffs; + // Internals + wf_heuristic->steps_wait = steps_between_cutoffs; +} +void wavefront_heuristic_set_xdrop( + wavefront_heuristic_t* const wf_heuristic, + const int xdrop, + const int steps_between_cutoffs) { + wf_heuristic->strategy |= wf_heuristic_xdrop; + wf_heuristic->xdrop = xdrop; + wf_heuristic->steps_between_cutoffs = steps_between_cutoffs; + // Internals + wf_heuristic->steps_wait = steps_between_cutoffs; + wf_heuristic->max_sw_score = 0; + wf_heuristic->max_sw_score_offset = WAVEFRONT_OFFSET_NULL; + wf_heuristic->max_sw_score_k = DPMATRIX_DIAGONAL_NULL; +} +void wavefront_heuristic_set_zdrop( + wavefront_heuristic_t* const wf_heuristic, + const int zdrop, + const int steps_between_cutoffs) { + wf_heuristic->strategy |= wf_heuristic_zdrop; + wf_heuristic->zdrop = zdrop; + wf_heuristic->steps_between_cutoffs = steps_between_cutoffs; + // Internals + wf_heuristic->steps_wait = steps_between_cutoffs; + wf_heuristic->max_sw_score = 0; + wf_heuristic->max_sw_score_offset = WAVEFRONT_OFFSET_NULL; + wf_heuristic->max_sw_score_k = DPMATRIX_DIAGONAL_NULL; +} +void wavefront_heuristic_set_banded_static( + wavefront_heuristic_t* const wf_heuristic, + const int band_min_k, + const int band_max_k) { + wf_heuristic->strategy |= wf_heuristic_banded_static; + wf_heuristic->min_k = band_min_k; + wf_heuristic->max_k = band_max_k; +} +void wavefront_heuristic_set_banded_adaptive( + wavefront_heuristic_t* const wf_heuristic, + const int band_min_k, + const int band_max_k, + const int steps_between_cutoffs) { + wf_heuristic->strategy |= wf_heuristic_banded_adaptive; + wf_heuristic->min_k = band_min_k; + wf_heuristic->max_k = band_max_k; + wf_heuristic->steps_between_cutoffs = steps_between_cutoffs; + // Internals + wf_heuristic->steps_wait = steps_between_cutoffs; +} +void wavefront_heuristic_clear( + wavefront_heuristic_t* const wf_heuristic) { + // Internals + wf_heuristic->steps_wait = wf_heuristic->steps_between_cutoffs; + wf_heuristic->max_sw_score = 0; + wf_heuristic->max_sw_score_offset = WAVEFRONT_OFFSET_NULL; + wf_heuristic->max_sw_score_k = DPMATRIX_DIAGONAL_NULL; +} +/* + * Utils + */ +int wf_distance_end2end( + const wf_offset_t offset, + const int k, + const int pattern_length, + const int text_length) { + const int left_v = pattern_length - WAVEFRONT_V(k,offset); + const int left_h = text_length - WAVEFRONT_H(k,offset); + return (offset >= 0) ? MAX(left_v,left_h) : -WAVEFRONT_OFFSET_NULL; +} +int wf_distance_end2end_weighted( + const wf_offset_t offset, + const int k, + const int pattern_length, + const int text_length, + const int mfactor) { + const int v = WAVEFRONT_V(k,offset); + const int h = WAVEFRONT_H(k,offset); + const int left_v = ((float)(pattern_length - v)/pattern_length * mfactor); + const int left_h = ((float)(text_length - h)/text_length * mfactor); + return (offset >= 0) ? MAX(left_v,left_h) : -WAVEFRONT_OFFSET_NULL; +} +int wf_distance_endsfree( + const wf_offset_t offset, + const int k, + const int pattern_length, + const int text_length, + const int pattern_end_free, + const int text_end_free) { + const int left_v = pattern_length - WAVEFRONT_V(k,offset); + const int left_h = text_length - WAVEFRONT_H(k,offset); + const int left_v_endsfree = left_v - pattern_end_free; + const int left_h_endsfree = left_h - text_end_free; + const int dist_up = MAX(left_h,left_v_endsfree); + const int dist_down = MAX(left_v,left_h_endsfree); + return (offset >= 0) ? MIN(dist_up,dist_down) : -WAVEFRONT_OFFSET_NULL; +} +void wf_heuristic_equate( + wavefront_t* const wavefront_dst, + wavefront_t* const wavefront_src) { + if (wavefront_dst != NULL) { + if (wavefront_src->lo > wavefront_dst->lo) wavefront_dst->lo = wavefront_src->lo; + if (wavefront_src->hi < wavefront_dst->hi) wavefront_dst->hi = wavefront_src->hi; + if (wavefront_dst->lo > wavefront_dst->hi) wavefront_dst->null = true; + // Save min/max WF initialized + wavefront_dst->wf_elements_init_min = wavefront_dst->lo; + wavefront_dst->wf_elements_init_max = wavefront_dst->hi; + } +} +/* + * Heuristic Cut-off Wavefront-Adaptive + */ +int wf_compute_distance_end2end( + wavefront_t* const wavefront, + const int pattern_length, + const int text_length, + wf_offset_t* const distances) { + // Compute min-distance + const wf_offset_t* const offsets = wavefront->offsets; + int k, min_distance = MAX(pattern_length,text_length); + PRAGMA_LOOP_VECTORIZE + for (k=wavefront->lo;k<=wavefront->hi;++k) { + const int distance = wf_distance_end2end( + offsets[k],k,pattern_length,text_length); + distances[k] = distance; + min_distance = MIN(min_distance,distance); + } + return min_distance; +} +int wf_compute_distance_end2end_weighted( + wavefront_t* const wavefront, + const int pattern_length, + const int text_length, + wf_offset_t* const distances) { + // Parameters + const int mfactor = ((float)(pattern_length + text_length) / 2); // Mean sequence length + // Compute min-distance + const wf_offset_t* const offsets = wavefront->offsets; + int k, min_distance = MAX(pattern_length,text_length); + PRAGMA_LOOP_VECTORIZE + for (k=wavefront->lo;k<=wavefront->hi;++k) { + const int distance = wf_distance_end2end_weighted( + offsets[k],k,pattern_length,text_length,mfactor); + distances[k] = distance; + min_distance = MIN(min_distance,distance); + } + return min_distance; +} +int wf_compute_distance_endsfree( + wavefront_t* const wavefront, + const int pattern_length, + const int text_length, + const int pattern_end_free, + const int text_end_free, + wf_offset_t* const distances) { + // Compute min-distance + const wf_offset_t* const offsets = wavefront->offsets; + int k, min_distance = MAX(pattern_length,text_length); + PRAGMA_LOOP_VECTORIZE + for (k=wavefront->lo;k<=wavefront->hi;++k) { + const int distance = wf_distance_endsfree( + offsets[k],k,pattern_length,text_length, + pattern_end_free,text_end_free); + distances[k] = distance; + min_distance = MIN(min_distance,distance); + } + return min_distance; +} +void wf_heuristic_wfadaptive_reduce( + wavefront_t* const wavefront, + const wf_offset_t* const distances, + const int min_distance, + const int max_distance_threshold, + const int min_k, + const int max_k) { + int k; + // Reduce from bottom + const int top_limit = MIN(max_k,wavefront->hi); // Preserve target-diagonals + int lo_reduced = wavefront->lo; + for (k=wavefront->lo;klo = lo_reduced; + // Reduce from top + const int botton_limit = MAX(min_k,wavefront->lo); // Preserve target-diagonals + int hi_reduced = wavefront->hi; + for (k=wavefront->hi;k>botton_limit;--k) { + if (distances[k] - min_distance <= max_distance_threshold) break; + --hi_reduced; + } + wavefront->hi = hi_reduced; +} +void wavefront_heuristic_wfadaptive( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront, + const bool wfmash_mode) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + const int min_wavefront_length = wf_aligner->heuristic.min_wavefront_length; + const int max_distance_threshold = wf_aligner->heuristic.max_distance_threshold; + wavefront_heuristic_t* const wf_heuristic = &wf_aligner->heuristic; + // Check steps + if (wf_heuristic->steps_wait > 0) return; + // Check minimum wavefront length + const int base_hi = wavefront->hi; + const int base_lo = wavefront->lo; + if ((base_hi - base_lo + 1) < min_wavefront_length) return; + // Use victim as temporal buffer + wavefront_components_resize_null__victim(&wf_aligner->wf_components,base_lo-1,base_hi+1); + wf_offset_t* const distances = wf_aligner->wf_components.wavefront_victim->offsets; + // Compute distance & cut-off + int min_distance; + if (wfmash_mode) { + min_distance = wf_compute_distance_end2end_weighted( + wavefront,pattern_length,text_length,distances); + } else { + min_distance = wf_compute_distance_end2end( + wavefront,pattern_length,text_length,distances); + } + // Cut-off wavefront + const int alignment_k = DPMATRIX_DIAGONAL(text_length,pattern_length); + wf_heuristic_wfadaptive_reduce( + wavefront,distances,min_distance,max_distance_threshold, + alignment_k,alignment_k); + // Set wait steps (don't repeat this heuristic often) + wf_heuristic->steps_wait = wf_heuristic->steps_between_cutoffs; +} +/* + * Heuristic Cut-off Drops + */ +void wf_heuristic_compute_sw_scores( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront, + const int wf_score, + wf_offset_t* const sw_scores, + wf_offset_t* const max_sw_score, + wf_offset_t* const max_k, + wf_offset_t* const max_offset) { + // Parameters + const int wf_match = wf_aligner->penalties.match; + const int swg_match = (wf_match==0) ? 1 : -(wf_aligner->penalties.match); + // Compute min-distance + const wf_offset_t* const offsets = wavefront->offsets; + int k, cmax_sw_score = INT_MIN, cmax_k = 0, cmax_offset = 0; + PRAGMA_LOOP_VECTORIZE + for (k=wavefront->lo;k<=wavefront->hi;++k) { + const wf_offset_t offset = offsets[k]; + if (offset < 0) continue; + const int v = WAVEFRONT_V(k,offset); + const int h = WAVEFRONT_H(k,offset); + const int sw_score = (wf_match==0) ? + (swg_match*(v+h) - wf_score) : + WF_SCORE_TO_SW_SCORE(swg_match,v,h,wf_score); + sw_scores[k] = sw_score; + if (cmax_sw_score < sw_score) { + cmax_sw_score = sw_score; + cmax_k = k; + cmax_offset = offset; + } + } + *max_sw_score = cmax_sw_score; + *max_k = cmax_k; + *max_offset = cmax_offset; +} +void wavefront_heuristic_xdrop( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront, + const int score) { + // Parameters + wavefront_heuristic_t* const wf_heuristic = &wf_aligner->heuristic; + const int base_hi = wavefront->hi; + const int base_lo = wavefront->lo; + // Check steps + if (wf_heuristic->steps_wait > 0) return; + // Use victim as temporal buffer + wavefront_components_resize_null__victim(&wf_aligner->wf_components,base_lo-1,base_hi+1); + wf_offset_t* const sw_scores = wf_aligner->wf_components.wavefront_victim->offsets; + // Compute SW scores + wf_offset_t cmax_sw_score, cmax_k, dummy; + wf_heuristic_compute_sw_scores( + wf_aligner,wavefront,score,sw_scores, + &cmax_sw_score,&cmax_k,&dummy); + // Apply X-Drop + const int xdrop = wf_heuristic->xdrop; + const int max_sw_score = wf_heuristic->max_sw_score; + const wf_offset_t* const offsets = wavefront->offsets; + if (wf_heuristic->max_sw_score_k != DPMATRIX_DIAGONAL_NULL) { + // Reduce from bottom + int k; + for (k=wavefront->lo;k<=wavefront->hi;++k) { + if (offsets[k] < 0) continue; + //fprintf(stderr,"[XDROP] (max=%d,current=%d) diff=%d leeway=%d\n", + // max_sw_score,(int)sw_scores[k], + // max_sw_score - (int)sw_scores[k],xdrop); + if (max_sw_score - (int)sw_scores[k] < xdrop) break; + } + wavefront->lo = k; + // Reduce from top + for (k=wavefront->hi;k>=wavefront->lo;--k) { + if (offsets[k] < 0) continue; + //fprintf(stderr,"[XDROP] (max=%d,current=%d) diff=%d leeway=%d\n", + // max_sw_score,(int)sw_scores[k], + // max_sw_score - (int)sw_scores[k],xdrop); + if (max_sw_score - (int)sw_scores[k] < xdrop) break; + } + wavefront->hi = k; + // Update maximum score observed + if (cmax_sw_score > wf_heuristic->max_sw_score) { + wf_heuristic->max_sw_score = cmax_sw_score; + wf_heuristic->max_sw_score_k = cmax_k; + } + } else { + // Update maximum score observed + wf_heuristic->max_sw_score = cmax_sw_score; + wf_heuristic->max_sw_score_k = cmax_k; + } + // Set wait steps (don't repeat this heuristic often) + wf_heuristic->steps_wait = wf_heuristic->steps_between_cutoffs; +} +int wf_zdrop_gap_score( + const int gap_extension_penalty, + const wf_offset_t offset_1, + const int k_1, + const wf_offset_t offset_2, + const int k_2) { + int diff_h = WAVEFRONT_H(k_2,offset_2) - WAVEFRONT_H(k_1,offset_1); + if (diff_h < 0) diff_h = -diff_h; + int diff_v = WAVEFRONT_V(k_2,offset_2) - WAVEFRONT_V(k_1,offset_1); + if (diff_v < 0) diff_v = -diff_v; + const int gap_length = (diff_h >= diff_v) ? diff_h-diff_v : diff_v-diff_h; + return gap_length * gap_extension_penalty; +} +void wavefront_heuristic_zdrop( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront, + const int score) { + // Parameters + wavefront_heuristic_t* const wf_heuristic = &wf_aligner->heuristic; + const int base_hi = wavefront->hi; + const int base_lo = wavefront->lo; + // Check steps + if (wf_heuristic->steps_wait > 0) return; + // Use victim as temporal buffer + wavefront_components_resize_null__victim(&wf_aligner->wf_components,base_lo-1,base_hi+1); + wf_offset_t* const sw_scores = wf_aligner->wf_components.wavefront_victim->offsets; + // Compute SW scores + wf_offset_t cmax_sw_score, cmax_k, cmax_offset; + wf_heuristic_compute_sw_scores( + wf_aligner,wavefront,score,sw_scores, + &cmax_sw_score,&cmax_k,&cmax_offset); + // Apply Z-Drop + wavefront_penalties_t* const penalties = &wf_aligner->penalties; + const int gap_e = (penalties->gap_extension1 > 0) ? penalties->gap_extension1 : 1; + const int zdrop = wf_heuristic->zdrop; + const int max_sw_score = wf_heuristic->max_sw_score; + const int max_k = wf_heuristic->max_sw_score_k; + const int max_offset = wf_heuristic->max_sw_score_offset; + if (max_k != DPMATRIX_DIAGONAL_NULL) { + // Update maximum score observed + if (cmax_sw_score > wf_heuristic->max_sw_score) { + wf_heuristic->max_sw_score = cmax_sw_score; + wf_heuristic->max_sw_score_k = cmax_k; + wf_heuristic->max_sw_score_offset = cmax_offset; + } else { + // Test Z-drop + const int gap_score = wf_zdrop_gap_score(gap_e,max_offset,max_k,cmax_offset,cmax_k); + // fprintf(stderr,"[Z-DROP] (max=%d~(%d,%d),current=%d~(%d,%d)) diff=%d leeway=%d\n", + // max_sw_score,WAVEFRONT_V(max_k,max_offset),WAVEFRONT_H(max_k,max_offset), + // cmax_sw_score,WAVEFRONT_V(cmax_k,cmax_offset),WAVEFRONT_H(cmax_k,cmax_offset), + // max_sw_score - cmax_sw_score, + // zdrop + gap_score); + if (max_sw_score - (int)cmax_sw_score > zdrop + gap_score) { + wavefront->lo = wavefront->hi + 1; + return; // Z-dropped + } + } + } else { + // Update maximum score observed + wf_heuristic->max_sw_score = cmax_sw_score; + wf_heuristic->max_sw_score_k = cmax_k; + wf_heuristic->max_sw_score_offset = cmax_offset; + } + // Set wait steps (don't repeat this heuristic often) + wf_heuristic->steps_wait = wf_heuristic->steps_between_cutoffs; +} +/* + * Heuristic Cut-off Banded + */ +void wavefront_heuristic_banded_static( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront) { + // Parameters + wavefront_heuristic_t* const wf_heuristic = &wf_aligner->heuristic; + // Check wavefront limits + if (wavefront->lo < wf_heuristic->min_k) wavefront->lo = wf_heuristic->min_k; + if (wavefront->hi > wf_heuristic->max_k) wavefront->hi = wf_heuristic->max_k; +} +void wavefront_heuristic_banded_adaptive( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + wavefront_heuristic_t* const wf_heuristic = &wf_aligner->heuristic; + // Check steps + if (wf_heuristic->steps_wait > 0) return; + // Check wavefront length + const int lo = wavefront->lo; + const int hi = wavefront->hi; + const int wf_length = hi - lo + 1; + if (wf_length < 4) return; // We cannot do anything here + // Adjust the band + const wf_offset_t* const offsets = wavefront->offsets; + const int max_wf_length = wf_heuristic->max_k - wf_heuristic->min_k + 1; + if (wf_length > max_wf_length) { + // Sample wavefront + const int leeway = (wf_length - max_wf_length) / 2; + const int quarter = wf_length / 4; + const int dist_p0 = wf_distance_end2end( + offsets[lo],lo,pattern_length,text_length); + const int dist_p1 = wf_distance_end2end( + offsets[lo+quarter],lo+quarter,pattern_length,text_length); + const int dist_p2 = wf_distance_end2end( + offsets[lo+2*quarter],lo+2*quarter,pattern_length,text_length); + const int dist_p3 = wf_distance_end2end( + offsets[hi],hi,pattern_length,text_length); + // Heuristically decide where to place the band + int new_lo = lo; + if (dist_p0 > dist_p3) new_lo += leeway; + if (dist_p1 > dist_p2) new_lo += leeway; + // Set wavefront limits + wavefront->lo = new_lo; + if (wavefront->lo < lo) wavefront->lo = lo; + wavefront->hi = new_lo + max_wf_length - 1; + if (wavefront->hi > hi) wavefront->hi = hi; + } + // Set wait steps (don't repeat this heuristic often) + wf_heuristic->steps_wait = wf_heuristic->steps_between_cutoffs; +} +/* + * Heuristic Cut-offs dispatcher + */ +void wavefront_heuristic_cufoff( + wavefront_aligner_t* const wf_aligner, + const int score, + const int score_mod) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + wavefront_heuristic_t* const wf_heuristic = &wf_aligner->heuristic; + // Fetch m-wavefront + wavefront_t* const mwavefront = wf_components->mwavefronts[score_mod]; + if (mwavefront == NULL || mwavefront->lo > mwavefront->hi) return; + // Decrease wait steps + --(wf_heuristic->steps_wait); + // Select heuristic (WF-Adaptive) + if (wf_heuristic->strategy & wf_heuristic_wfadaptive) { + wavefront_heuristic_wfadaptive(wf_aligner,mwavefront,false); + } else if (wf_heuristic->strategy & wf_heuristic_wfmash) { + wavefront_heuristic_wfadaptive(wf_aligner,mwavefront,true); + } + // Select heuristic (Drops) + if (wf_heuristic->strategy & wf_heuristic_xdrop) { + wavefront_heuristic_xdrop(wf_aligner,mwavefront,score); + } else if (wf_heuristic->strategy & wf_heuristic_zdrop) { + wavefront_heuristic_zdrop(wf_aligner,mwavefront,score); + } + // Select heuristic (Banded) + if (wf_heuristic->strategy & wf_heuristic_banded_static) { + wavefront_heuristic_banded_static(wf_aligner,mwavefront); + } else if (wf_heuristic->strategy & wf_heuristic_banded_adaptive) { + wavefront_heuristic_banded_adaptive(wf_aligner,mwavefront); + } + // Check wavefront length + if (mwavefront->lo > mwavefront->hi) mwavefront->null = true; + // DEBUG + // const int wf_length_base = hi_base-lo_base+1; + // const int wf_length_reduced = mwavefront->hi-mwavefront->lo+1; + // fprintf(stderr,"[WFA::Heuristic] Heuristic from %d to %d offsets (%2.2f%%)\n", + // wf_length_base,wf_length_reduced,100.0f*(float)wf_length_reduced/(float)wf_length_base); + // Save min/max WF initialized + mwavefront->wf_elements_init_min = mwavefront->lo; + mwavefront->wf_elements_init_max = mwavefront->hi; + // Equate other wavefronts + if (distance_metric <= gap_linear) return; + // Cut-off the other wavefronts (same dimensions as M) + wavefront_t* const i1wavefront = wf_components->i1wavefronts[score_mod]; + wavefront_t* const d1wavefront = wf_components->d1wavefronts[score_mod]; + wf_heuristic_equate(i1wavefront,mwavefront); + wf_heuristic_equate(d1wavefront,mwavefront); + if (distance_metric == gap_affine) return; + wavefront_t* const i2wavefront = wf_components->i2wavefronts[score_mod]; + wavefront_t* const d2wavefront = wf_components->d2wavefronts[score_mod]; + wf_heuristic_equate(i2wavefront,mwavefront); + wf_heuristic_equate(d2wavefront,mwavefront); +} +/* + * Display + */ +void wavefront_heuristic_print( + FILE* const stream, + wavefront_heuristic_t* const wf_heuristic) { + // Select heuristic strategy + if (wf_heuristic->strategy == wf_heuristic_none) { + fprintf(stream,"(none)"); + } else { + // WF-Adaptive + if (wf_heuristic->strategy & wf_heuristic_wfadaptive) { + fprintf(stream,"(wfadapt,%d,%d,%d)", + wf_heuristic->min_wavefront_length, + wf_heuristic->max_distance_threshold, + wf_heuristic->steps_between_cutoffs); + } else if (wf_heuristic->strategy & wf_heuristic_wfmash) { + fprintf(stream,"(wfmash,%d,%d,%d)", + wf_heuristic->min_wavefront_length, + wf_heuristic->max_distance_threshold, + wf_heuristic->steps_between_cutoffs); + } + // Drops + if (wf_heuristic->strategy & wf_heuristic_xdrop) { + fprintf(stream,"(xdrop,%d,%d)", + wf_heuristic->xdrop, + wf_heuristic->steps_between_cutoffs); + } + if (wf_heuristic->strategy & wf_heuristic_zdrop) { + fprintf(stream,"(zdrop,%d,%d)", + wf_heuristic->zdrop, + wf_heuristic->steps_between_cutoffs); + } + // Banded + if (wf_heuristic->strategy & wf_heuristic_banded_static) { + fprintf(stream,"(banded-static,%d,%d)", + wf_heuristic->min_k, + wf_heuristic->max_k); + } + if (wf_heuristic->strategy & wf_heuristic_banded_adaptive) { + fprintf(stream,"(banded-adapt,%d,%d,%d)", + wf_heuristic->min_k, + wf_heuristic->max_k, + wf_heuristic->steps_between_cutoffs); + } + } +} diff --git a/src/lib/wfa2/wavefront/wavefront_heuristic.h b/src/lib/wfa2/wavefront/wavefront_heuristic.h new file mode 100644 index 000000000..7454f8d73 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_heuristic.h @@ -0,0 +1,126 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Support functions for wavefront heuristic strategies + */ + +#ifndef WAVEFRONT_HEURISTIC_H_ +#define WAVEFRONT_HEURISTIC_H_ + +#include "../utils/commons.h" + +// Wavefront ahead definition +typedef struct _wavefront_aligner_t wavefront_aligner_t; + +/* + * Wavefront Heuristics + */ +typedef enum { + wf_heuristic_none = 0x0000000000000000ul, + wf_heuristic_banded_static = 0x0000000000000001ul, + wf_heuristic_banded_adaptive = 0x0000000000000002ul, + wf_heuristic_wfadaptive = 0x0000000000000004ul, + wf_heuristic_xdrop = 0x0000000000000010ul, + wf_heuristic_zdrop = 0x0000000000000020ul, + wf_heuristic_wfmash = 0x0000000000000040ul, +} wf_heuristic_strategy; +typedef struct { + // Heuristic + wf_heuristic_strategy strategy; // Heuristic strategy + int steps_between_cutoffs; // Score-steps between heuristic cut-offs + // Static/Adaptive Banded + int min_k; // Banded: Minimum k to consider in band + int max_k; // Banded: Maximum k to consider in band + // WFAdaptive + int min_wavefront_length; // Adaptive: Minimum wavefronts length to cut-off + int max_distance_threshold; // Adaptive: Maximum distance between offsets allowed + // Drops + int xdrop; // X-drop parameter + int zdrop; // Z-drop parameter + // Internals + int steps_wait; // Score-steps until next cut-off + int max_sw_score; // Maximum score observed (for x/z drops) + int max_sw_score_offset; // Offset of the maximum score observed + int max_sw_score_k; // Diagonal of the maximum score observed +} wavefront_heuristic_t; + +/* + * Setup + */ +void wavefront_heuristic_set_none( + wavefront_heuristic_t* const wf_heuristic); + +void wavefront_heuristic_set_wfadaptive( + wavefront_heuristic_t* const wf_heuristic, + const int min_wavefront_length, + const int max_distance_threshold, + const int steps_between_cutoffs); +void wavefront_heuristic_set_wfmash( + wavefront_heuristic_t* const wf_heuristic, + const int min_wavefront_length, + const int max_distance_threshold, + const int steps_between_cutoffs); + +void wavefront_heuristic_set_xdrop( + wavefront_heuristic_t* const wf_heuristic, + const int xdrop, + const int steps_between_cutoffs); +void wavefront_heuristic_set_zdrop( + wavefront_heuristic_t* const wf_heuristic, + const int ydrop, + const int steps_between_cutoffs); + +void wavefront_heuristic_set_banded_static( + wavefront_heuristic_t* const wf_heuristic, + const int band_min_k, + const int band_max_k); +void wavefront_heuristic_set_banded_adaptive( + wavefront_heuristic_t* const wf_heuristic, + const int band_min_k, + const int band_max_k, + const int steps_between_cutoffs); + +void wavefront_heuristic_clear( + wavefront_heuristic_t* const wf_heuristic); + +/* + * Wavefront heuristic cut-off + */ +void wavefront_heuristic_cufoff( + wavefront_aligner_t* const wf_aligner, + const int score, + const int score_mod); + +/* + * Display + */ +void wavefront_heuristic_print( + FILE* const stream, + wavefront_heuristic_t* const wf_heuristic); + +#endif /* WAVEFRONT_HEURISTIC_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_offset.h b/src/lib/wfa2/wavefront/wavefront_offset.h new file mode 100644 index 000000000..473c560b2 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_offset.h @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront offset type and utils + */ + +#ifndef WAVEFRONT_OFFSET_H_ +#define WAVEFRONT_OFFSET_H_ + +#include "../utils/commons.h" + +/* + * Wavefront Offset + */ +typedef int32_t wf_offset_t; +typedef uint32_t wf_unsigned_offset_t; + +/* + * Constants + */ +#define WAVEFRONT_OFFSET_NULL (INT32_MIN/2) + +/* + * Translate k and offset to coordinates h,v + */ +#define WAVEFRONT_LENGTH(lo,hi) ((hi)-(lo)+1) // (lo/hi inclusive and +1 for WF[0]) +#define WAVEFRONT_V(k,offset) ((offset)-(k)) +#define WAVEFRONT_H(k,offset) (offset) +#define WAVEFRONT_ANTIDIAGONAL(k,offset) (2*(offset)-(k)) + +#define DPMATRIX_DIAGONAL_NULL INT_MAX +#define DPMATRIX_DIAGONAL(h,v) ((h)-(v)) +#define DPMATRIX_ANTIDIAGONAL(h,v) ((h)+(v)) +#define DPMATRIX_OFFSET(h,v) (h) + +#define WAVEFRONT_K_INVERSE(k,plen,tlen) ((tlen)-(plen)-(k)) + +#endif /* WAVEFRONT_OFFSET_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_pcigar.c b/src/lib/wfa2/wavefront/wavefront_pcigar.c new file mode 100644 index 000000000..a66e066ad --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_pcigar.c @@ -0,0 +1,277 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Packed CIGAR (Alignment operations in 2-bits) + */ + +#include "wavefront_pcigar.h" + +/* + * Packed CIGAR operations + */ +typedef struct { + char operation; + int inc_v; + int inc_h; + affine_matrix_type matrix_type; +} pcigar_op_t; +pcigar_op_t pcigar_lut[4] = { + { .operation = '?', .inc_v = 0, .inc_h = 0, .matrix_type = affine_matrix_M }, // 00 - None + { .operation = 'D', .inc_v = 1, .inc_h = 0, .matrix_type = affine_matrix_D }, // 01 - DELETION + { .operation = 'X', .inc_v = 1, .inc_h = 1, .matrix_type = affine_matrix_M }, // 10 - MISMATCH + { .operation = 'I', .inc_v = 0, .inc_h = 1, .matrix_type = affine_matrix_I }, // 11 - INSERTION +}; +// Precomputed string of Matches +char matches_lut[8] = "MMMMMMMM"; +#define CIGAR_8MATCHES_UINT64 *((uint64_t*)matches_lut) + +/* + * Accessors + */ +int pcigar_get_length( + const pcigar_t pcigar) { + int cigar_length = PCIGAR_MAX_LENGTH; + if (!PCIGAR_IS_UTILISED(pcigar,PCIGAR_FULL_MASK)) { + const int free_slots = PCIGAR_FREE_SLOTS(pcigar); + cigar_length -= free_slots; + } + return cigar_length; +} +int pcigar_unpack( + pcigar_t pcigar, + char* cigar_buffer) { + // Compute pcigar length and shift to the end of the word + int pcigar_length = PCIGAR_MAX_LENGTH; + if (!PCIGAR_IS_UTILISED(pcigar,PCIGAR_FULL_MASK)) { + const int free_slots = PCIGAR_FREE_SLOTS(pcigar); + pcigar_length -= free_slots; + pcigar <<= free_slots*2; + } + // Unpack BT-blocks + int i; + for (i=0;ioperation; + } + return pcigar_length; +} +/* + * PCIGAR extend exact-matches + */ +int pcigar_unpack_extend( + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + int v, + int h, + char* cigar_buffer) { + int num_matches = 0; + // Fetch pattern/text blocks + uint64_t* pattern_blocks = (uint64_t*)(pattern+v); + uint64_t* text_blocks = (uint64_t*)(text+h); + uint64_t pattern_block = *pattern_blocks; + uint64_t text_block = *text_blocks; + // Compare 64-bits blocks + uint64_t cmp = pattern_block ^ text_block; + while (cmp==0 && (v+8) < pattern_length && (h+8) < text_length) { + // Increment offset + v += 8; + h += 8; + num_matches += 8; + // Dump matches in block + *((uint64_t*)cigar_buffer) = CIGAR_8MATCHES_UINT64; + cigar_buffer += 8; + // Next blocks + ++pattern_blocks; + ++text_blocks; + // Fetch & Compare + pattern_block = *pattern_blocks; + text_block = *text_blocks; + cmp = pattern_block ^ text_block; + } + // Count equal characters + num_matches += __builtin_ctzl(cmp)/8; + *((uint64_t*)cigar_buffer) = CIGAR_8MATCHES_UINT64; + // Return total matches + return num_matches; +} +int pcigar_unpack_extend_custom( + const int pattern_length, + const int text_length, + alignment_match_funct_t const match_funct, + void* const match_funct_arguments, + int v, + int h, + char* cigar_buffer) { + int num_matches = 0; + while (v < pattern_length && h < text_length) { + // Check match + if (!match_funct(v,h,match_funct_arguments)) break; + ++v; ++h; + // Increment matches + *cigar_buffer = 'M'; + ++cigar_buffer; + ++num_matches; + } + return num_matches; +} +/* + * PCIGAR unpack + */ +void pcigar_unpack_linear( + pcigar_t pcigar, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + alignment_match_funct_t const match_funct, + void* const match_funct_arguments, + int* const v_pos, + int* const h_pos, + char* cigar_buffer, + int* const cigar_length) { + // Parameters + char* const cigar_buffer_base = cigar_buffer; + // Compute pcigar length and shift to the end of the word + int pcigar_length = PCIGAR_MAX_LENGTH; + if (!PCIGAR_IS_UTILISED(pcigar,PCIGAR_FULL_MASK)) { + const int free_slots = PCIGAR_FREE_SLOTS(pcigar); + pcigar_length -= free_slots; + pcigar <<= free_slots*2; + } + // Unpack BT-blocks + int v = *v_pos, h = *h_pos, i; + for (i=0;ioperation; + v += op->inc_v; + h += op->inc_h; + } + // Update length/positions + *cigar_length = cigar_buffer - cigar_buffer_base; + *v_pos = v; + *h_pos = h; +} +void pcigar_unpack_affine( + pcigar_t pcigar, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + alignment_match_funct_t const match_funct, + void* const match_funct_arguments, + int* const v_pos, + int* const h_pos, + char* cigar_buffer, + int* const cigar_length, + affine_matrix_type* const current_matrix_type) { + // Parameters + char* const cigar_buffer_base = cigar_buffer; + // Compute pcigar length and shift to the end of the word + int pcigar_length = PCIGAR_MAX_LENGTH; + if (!PCIGAR_IS_UTILISED(pcigar,PCIGAR_FULL_MASK)) { + const int free_slots = PCIGAR_FREE_SLOTS(pcigar); + pcigar_length -= free_slots; + pcigar <<= free_slots*2; + } + // Unpack BT-blocks + affine_matrix_type matrix_type = *current_matrix_type; + int v = *v_pos, h = *h_pos, i; + for (i=0;ioperation=='X') { + matrix_type = affine_matrix_M; + continue; + } + // Add operation + *(cigar_buffer++) = op->operation; + v += op->inc_v; + h += op->inc_h; + matrix_type = op->matrix_type; + } + // Update length/positions + *cigar_length = cigar_buffer - cigar_buffer_base; + *v_pos = v; + *h_pos = h; + *current_matrix_type = matrix_type; +} +/* + * Display + */ +void pcigar_print( + FILE* const stream, + pcigar_t pcigar) { + char cigar_buffer[64]; + const int pcigar_length = pcigar_unpack(pcigar,cigar_buffer); + cigar_buffer[pcigar_length] = '\0'; + fprintf(stream,"%s",cigar_buffer); +} diff --git a/src/lib/wfa2/wavefront/wavefront_pcigar.h b/src/lib/wfa2/wavefront/wavefront_pcigar.h new file mode 100644 index 000000000..4e02e187e --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_pcigar.h @@ -0,0 +1,125 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Packed CIGAR (Alignment operations in 2-bits) + */ + +#ifndef WAVEFRONT_PACKED_CIGAR_H_ +#define WAVEFRONT_PACKED_CIGAR_H_ + +#include "../utils/commons.h" +#include "wavefront_attributes.h" + +/* + * Configuration + */ +#define PCIGAR_32BITS +//#define PCIGAR_64BITS + +/* + * Packed CIGAR + */ +#define PCIGAR_NULL 0ul +#define PCIGAR_DELETION 1ul +#define PCIGAR_MISMATCH 2ul +#define PCIGAR_INSERTION 3ul + +#define PCIGAR_POP_FRONT(pcigar) pcigar = pcigar << 2 +#define PCIGAR_PUSH_BACK(pcigar,operation) ((pcigar<<2) | operation) + +#define PCIGAR_PUSH_BACK_INS(pcigar) ((pcigar<<2) | PCIGAR_INSERTION) +#define PCIGAR_PUSH_BACK_DEL(pcigar) ((pcigar<<2) | PCIGAR_DELETION) +#define PCIGAR_PUSH_BACK_MISMS(pcigar) ((pcigar<<2) | PCIGAR_MISMATCH) + +#ifdef PCIGAR_32BITS + typedef uint32_t pcigar_t; + #define PCIGAR_MAX_LENGTH 16 + #define PCIGAR_FULL_MASK 0x40000000u /* Completely full */ + #define PCIGAR_ALMOST_FULL_MASK 0x10000000u /* 15-slots busy or more */ + #define PCIGAR_HALF_FULL_MASK 0x00010000u /* 9-slots busy or more */ + #define PCIGAR_IS_UTILISED(pcigar,mask) ((pcigar) >= mask) + #define PCIGAR_EXTRACT(pcigar) ((pcigar) >> 30) + #define PCIGAR_FREE_SLOTS(pcigar) ((pcigar)!=0) ? __builtin_clz(pcigar)/2 : PCIGAR_MAX_LENGTH; +#else + typedef uint64_t pcigar_t; + #define PCIGAR_MAX_LENGTH 32 + #define PCIGAR_FULL_MASK 0x4000000000000000ul /* Completely full */ + #define PCIGAR_ALMOST_FULL_MASK 0x1000000000000000ul /* 31-slots busy or more */ + #define PCIGAR_HALF_FULL_MASK 0x0000000100000000ul /* 17-slots busy or more */ + #define PCIGAR_IS_UTILISED(pcigar,mask) ((pcigar) >= mask) + #define PCIGAR_EXTRACT(pcigar) ((pcigar) >> 62) + #define PCIGAR_FREE_SLOTS(pcigar) ((pcigar)!=0) ? __builtin_clzl(pcigar)/2 : PCIGAR_MAX_LENGTH; +#endif + + +/* + * Accessors + */ +int pcigar_get_length( + const pcigar_t pcigar); +int pcigar_unpack( + pcigar_t pcigar, + char* cigar_buffer); + +/* + * PCIGAR unpack + */ +void pcigar_unpack_linear( + pcigar_t pcigar, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + alignment_match_funct_t const match_funct, + void* const match_funct_arguments, + int* const v_pos, + int* const h_pos, + char* cigar_buffer, + int* const cigar_length); +void pcigar_unpack_affine( + pcigar_t pcigar, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + alignment_match_funct_t const match_funct, + void* const match_funct_arguments, + int* const v_pos, + int* const h_pos, + char* cigar_buffer, + int* const cigar_length, + affine_matrix_type* const current_matrix_type); + +/* + * Display + */ +void pcigar_print( + FILE* const stream, + pcigar_t pcigar); + +#endif /* WAVEFRONT_PACKED_CIGAR_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_penalties.c b/src/lib/wfa2/wavefront/wavefront_penalties.c new file mode 100644 index 000000000..8e4b012e5 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_penalties.c @@ -0,0 +1,201 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront penalties handling module + */ + +#include "wavefront_penalties.h" + +/* + * Penalties adjustment + */ +void wavefront_penalties_set_indel( + wavefront_penalties_t* const wf_penalties) { + // Set distance model + wf_penalties->distance_metric = indel; + // Set penalties + wf_penalties->match = 0; + wf_penalties->mismatch = -1; + wf_penalties->gap_opening1 = 1; + wf_penalties->gap_extension1 = -1; + wf_penalties->gap_opening2 = -1; + wf_penalties->gap_extension2 = -1; +} +void wavefront_penalties_set_edit( + wavefront_penalties_t* const wf_penalties) { + // Set distance model + wf_penalties->distance_metric = edit; + // Set penalties + wf_penalties->match = 0; + wf_penalties->mismatch = 1; + wf_penalties->gap_opening1 = 1; + wf_penalties->gap_extension1 = -1; + wf_penalties->gap_opening2 = -1; + wf_penalties->gap_extension2 = -1; +} +void wavefront_penalties_set_linear( + wavefront_penalties_t* const wf_penalties, + linear_penalties_t* const linear_penalties) { + // Set distance model + wf_penalties->distance_metric = gap_linear; + // Check base penalties + if (linear_penalties->match > 0) { + fprintf(stderr,"[WFA::Penalties] Match score must be negative or zero (M=%d)\n",linear_penalties->match); + exit(1); + } else if (linear_penalties->mismatch <= 0 || linear_penalties->indel <= 0) { + fprintf(stderr,"[WFA::Penalties] Penalties (X=%d,D=%d,I=%d) must be (X>0,D>0,I>0)\n", + linear_penalties->mismatch,linear_penalties->indel,linear_penalties->indel); + exit(1); + } + // Set penalties (if needed, adjust using Eizenga's formula) + if (linear_penalties->match < 0) { + wf_penalties->match = linear_penalties->match; + wf_penalties->mismatch = 2*linear_penalties->mismatch - 2*linear_penalties->match; + wf_penalties->gap_opening1 = 2*linear_penalties->indel - linear_penalties->match; + } else { + wf_penalties->match = 0; + wf_penalties->mismatch = linear_penalties->mismatch; + wf_penalties->gap_opening1 = linear_penalties->indel; + } + // Set unused + wf_penalties->gap_extension1 = -1; + wf_penalties->gap_opening2 = -1; + wf_penalties->gap_extension2 = -1; +} +void wavefront_penalties_set_affine( + wavefront_penalties_t* const wf_penalties, + affine_penalties_t* const affine_penalties) { + // Set distance model + wf_penalties->distance_metric = gap_affine; + // Check base penalties + if (affine_penalties->match > 0) { + fprintf(stderr,"[WFA::Penalties] Match score must be negative or zero (M=%d)\n",affine_penalties->match); + exit(1); + } else if (affine_penalties->mismatch <= 0 || + affine_penalties->gap_opening < 0 || + affine_penalties->gap_extension <= 0) { + fprintf(stderr,"[WFA::Penalties] Penalties (X=%d,O=%d,E=%d) must be (X>0,O>=0,E>0)\n", + affine_penalties->mismatch, + affine_penalties->gap_opening, + affine_penalties->gap_extension); + exit(1); + } + // Set penalties (if needed, adjust using Eizenga's formula) + if (affine_penalties->match < 0) { + wf_penalties->match = affine_penalties->match; + wf_penalties->mismatch = 2*affine_penalties->mismatch - 2*affine_penalties->match; + wf_penalties->gap_opening1 = 2*affine_penalties->gap_opening; + wf_penalties->gap_extension1 = 2*affine_penalties->gap_extension - affine_penalties->match; + } else { + wf_penalties->match = 0; + wf_penalties->mismatch = affine_penalties->mismatch; + wf_penalties->gap_opening1 = affine_penalties->gap_opening; + wf_penalties->gap_extension1 = affine_penalties->gap_extension; + } + // Set unused + wf_penalties->gap_opening2 = -1; + wf_penalties->gap_extension2 = -1; +} +void wavefront_penalties_set_affine2p( + wavefront_penalties_t* const wf_penalties, + affine2p_penalties_t* const affine2p_penalties) { + // Set distance model + wf_penalties->distance_metric = gap_affine_2p; + // Check base penalties + if (affine2p_penalties->match > 0) { + fprintf(stderr,"[WFA::Penalties] Match score must be negative or zero (M=%d)\n",affine2p_penalties->match); + exit(1); + } else if (affine2p_penalties->mismatch <= 0 || + affine2p_penalties->gap_opening1 <= 0 || + affine2p_penalties->gap_extension1 <= 0 || + affine2p_penalties->gap_opening2 <= 0 || + affine2p_penalties->gap_extension2 <= 0) { + fprintf(stderr,"[WFA::Penalties] Penalties (X=%d,O1=%d,E1=%d,O2=%d,E2=%d) must be (X>0,O1>=0,E1>0,O1>=0,E1>0)\n", + affine2p_penalties->mismatch, + affine2p_penalties->gap_opening1, + affine2p_penalties->gap_extension1, + affine2p_penalties->gap_opening2, + affine2p_penalties->gap_extension2); + exit(1); + } + // Set penalties (if needed, adjust using Eizenga's formula) + if (affine2p_penalties->match < 0) { + wf_penalties->match = affine2p_penalties->match; + wf_penalties->mismatch = 2*affine2p_penalties->mismatch - 2*wf_penalties->match; + wf_penalties->gap_opening1 = 2*affine2p_penalties->gap_opening1; + wf_penalties->gap_extension1 = 2*affine2p_penalties->gap_extension1 - affine2p_penalties->match; + wf_penalties->gap_opening2 = 2*affine2p_penalties->gap_opening2; + wf_penalties->gap_extension2 = 2*affine2p_penalties->gap_extension2 - affine2p_penalties->match; + } else { + wf_penalties->match = 0; + wf_penalties->mismatch = affine2p_penalties->mismatch; + wf_penalties->gap_opening1 = affine2p_penalties->gap_opening1; + wf_penalties->gap_extension1 = affine2p_penalties->gap_extension1; + wf_penalties->gap_opening2 = affine2p_penalties->gap_opening2; + wf_penalties->gap_extension2 = affine2p_penalties->gap_extension2; + } +} +/* + * Display + */ +void wavefront_penalties_print( + FILE* const stream, + wavefront_penalties_t* const wf_penalties) { + // Select penalties mode + switch (wf_penalties->distance_metric) { + case indel: + fprintf(stream,"(Indel)"); + break; + case edit: + fprintf(stream,"(Edit)"); + break; + case gap_linear: + fprintf(stream,"(GapLinear,%d,%d)", + wf_penalties->mismatch, + wf_penalties->gap_opening1); + break; + case gap_affine: + fprintf(stream,"(GapAffine,%d,%d,%d)", + wf_penalties->mismatch, + wf_penalties->gap_opening1, + wf_penalties->gap_extension1); + break; + case gap_affine_2p: + fprintf(stream,"(GapAffine2p,%d,%d,%d,%d,%d)", + wf_penalties->mismatch, + wf_penalties->gap_opening1, + wf_penalties->gap_extension1, + wf_penalties->gap_opening2, + wf_penalties->gap_extension2); + break; + default: + break; + } +} + + diff --git a/src/lib/wfa2/wavefront/wavefront_penalties.h b/src/lib/wfa2/wavefront/wavefront_penalties.h new file mode 100644 index 000000000..7c50e6f43 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_penalties.h @@ -0,0 +1,111 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront penalties handling module + */ + +#ifndef WAVEFRONT_WAVEFRONT_PENALTIES_H_ +#define WAVEFRONT_WAVEFRONT_PENALTIES_H_ + +#include "../alignment/linear_penalties.h" +#include "../alignment/affine_penalties.h" +#include "../alignment/affine2p_penalties.h" + +/* + * Distance metrics + */ +typedef enum { + indel = 0, // Longest Common Subsequence - LCS + edit = 1, // Levenshtein + gap_linear = 2, // Needleman-Wunsch + gap_affine = 3, // Smith-Waterman-Gotoh + gap_affine_2p = 4 // Gap-Affine 2-pieces +} distance_metric_t; + +/* + * Wavefront Penalties + */ +typedef struct { + distance_metric_t distance_metric; // Alignment metric/distance used + int match; // (M <= 0) (Internal variable change to M=0 for WFA) + int mismatch; // (X > 0) + int gap_opening1; // (O1 >= 0) + int gap_extension1; // (E1 > 0) + int gap_opening2; // (O2 >= 0) + int gap_extension2; // (E2 > 0) +} wavefront_penalties_t; + +/* + * Compute SW-score equivalent (thanks to Eizenga's formula) + */ +#define WF_SCORE_TO_SW_SCORE(swg_match,plen,tlen,wf_score) ((swg_match*(plen+tlen) - wf_score)/2) + +/* + * Penalties adjustment + */ +void wavefront_penalties_set_indel( + wavefront_penalties_t* const wf_penalties); +void wavefront_penalties_set_edit( + wavefront_penalties_t* const wf_penalties); +void wavefront_penalties_set_linear( + wavefront_penalties_t* const wf_penalties, + linear_penalties_t* const linear_penalties); +void wavefront_penalties_set_affine( + wavefront_penalties_t* const wf_penalties, + affine_penalties_t* const affine_penalties); +void wavefront_penalties_set_affine2p( + wavefront_penalties_t* const wf_penalties, + affine2p_penalties_t* const affine2p_penalties); + +/* + * Score conversion + */ +int wavefront_penalties_get_score_indel( + wavefront_penalties_t* const wf_penalties, + const int score); +int wavefront_penalties_get_score_edit( + wavefront_penalties_t* const wf_penalties, + const int score); +int wavefront_penalties_get_score_linear( + wavefront_penalties_t* const wf_penalties, + const int score); +int wavefront_penalties_get_score_affine( + wavefront_penalties_t* const wf_penalties, + const int score); +int wavefront_penalties_get_score_affine2p( + wavefront_penalties_t* const wf_penalties, + const int score); + +/* + * Display + */ +void wavefront_penalties_print( + FILE* const stream, + wavefront_penalties_t* const wf_penalties); + +#endif /* WAVEFRONT_WAVEFRONT_PENALTIES_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_plot.c b/src/lib/wfa2/wavefront/wavefront_plot.c new file mode 100644 index 000000000..bd40297cd --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_plot.c @@ -0,0 +1,305 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Wavefront alignment module for plot + */ + +#include "wavefront_plot.h" +#include "wavefront_aligner.h" + +/* + * Heatmaps + */ +void wavefront_plot_heatmaps_allocate( + wavefront_plot_t* const wf_plot, + const int pattern_length, + const int text_length) { + wavefront_plot_attr_t* const attributes = &wf_plot->attributes; + // Compute dimensions + const int resolution_points = attributes->resolution_points; + const int min_v = (wf_plot->min_v == -1) ? 0 : wf_plot->min_v; + const int max_v = (wf_plot->max_v == -1) ? pattern_length-1 : wf_plot->max_v; + const int min_h = (wf_plot->min_h == -1) ? 0 : wf_plot->min_h; + const int max_h = (wf_plot->max_h == -1) ? text_length-1 : wf_plot->max_h; + // Behavior + wf_plot->behavior_heatmap = heatmap_new(heatmap_value, + min_v,max_v,min_h,max_h,resolution_points); + // Wavefront Components + wf_plot->m_heatmap = heatmap_new(heatmap_min, + min_v,max_v,min_h,max_h,resolution_points); + wf_plot->i1_heatmap = NULL; + wf_plot->d1_heatmap = NULL; + wf_plot->i2_heatmap = NULL; + wf_plot->d2_heatmap = NULL; + if (wf_plot->distance_metric < gap_affine) return; + // Gap-affine + wf_plot->i1_heatmap = heatmap_new(heatmap_min, + min_v,max_v,min_h,max_h,resolution_points); + wf_plot->d1_heatmap = heatmap_new(heatmap_min, + min_v,max_v,min_h,max_h,resolution_points); + if (wf_plot->distance_metric == gap_affine) return; + // Gap-affine-2p + wf_plot->i2_heatmap = heatmap_new(heatmap_min, + min_v,max_v,min_h,max_h,resolution_points); + wf_plot->d2_heatmap = heatmap_new(heatmap_min, + min_v,max_v,min_h,max_h,resolution_points); +} +void wavefront_plot_heatmaps_free( + wavefront_plot_t* const wf_plot) { + heatmap_delete(wf_plot->behavior_heatmap); + heatmap_delete(wf_plot->m_heatmap); + if (wf_plot->i1_heatmap) heatmap_delete(wf_plot->i1_heatmap); + if (wf_plot->d1_heatmap) heatmap_delete(wf_plot->d1_heatmap); + if (wf_plot->i2_heatmap) heatmap_delete(wf_plot->i2_heatmap); + if (wf_plot->d2_heatmap) heatmap_delete(wf_plot->d2_heatmap); +} +/* + * Setup + */ +wavefront_plot_t* wavefront_plot_new( + const distance_metric_t distance_metric, + const int pattern_length, + const int text_length, + wavefront_plot_attr_t* const attributes) { + // Handler + wavefront_plot_t* const wf_plot = (wavefront_plot_t*)malloc(sizeof(wavefront_plot_t)); + // Parameters + wf_plot->attributes = *attributes; + wf_plot->distance_metric = distance_metric; + wf_plot->min_v = -1; + wf_plot->max_v = -1; + wf_plot->min_h = -1; + wf_plot->max_h = -1; + // Allocate and configure + wavefront_plot_heatmaps_allocate(wf_plot,pattern_length,text_length); + // Clear offsets + wf_plot->offset_h = 0; + wf_plot->offset_v = 0; + // Return + return wf_plot; +} +void wavefront_plot_resize( + wavefront_plot_t* const wf_plot, + const int pattern_length, + const int text_length) { + // Free heatmaps + wavefront_plot_heatmaps_free(wf_plot); + // Allocate new heatmaps + wavefront_plot_heatmaps_allocate(wf_plot,pattern_length,text_length); + // Clear offsets + wf_plot->offset_h = 0; + wf_plot->offset_v = 0; +} +void wavefront_plot_delete( + wavefront_plot_t* const wf_plot) { + // Heatmaps + wavefront_plot_heatmaps_free(wf_plot); + // Handler + free(wf_plot); +} +/* + * Accessors + */ +void wavefront_plot_component( + wavefront_aligner_t* const wf_aligner, + wavefront_t* const wavefront, + const int score, + heatmap_t* const wf_heatmap, + const bool extend) { + // Check wavefront + if (wavefront == NULL) return; + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + const char* const pattern = wf_aligner->pattern; + const char* const text = wf_aligner->text; + wavefront_plot_t* const plot = wf_aligner->plot; + const bool reverse = (wf_aligner->align_mode == wf_align_biwfa_breakpoint_reverse); + // Traverse all offsets + int k; + for (k=wavefront->lo;k<=wavefront->hi;++k) { + const wf_offset_t offset = wavefront->offsets[k]; + if (offset < 0) continue; + // Compute local coordinates + int v_local = WAVEFRONT_V(k,offset); + int h_local = WAVEFRONT_H(k,offset); + if (v_local < 0 || v_local >= pattern_length) continue; + if (h_local < 0 || h_local >= text_length) continue; + // Compute global coordinates + int v_global, h_global; + if (reverse) { + v_global = plot->offset_v + (pattern_length - 1 - v_local); + h_global = plot->offset_h + (text_length - 1 - h_local); + } else { + v_global = plot->offset_v + v_local; + h_global = plot->offset_h + h_local; + } + // Plot + if (reverse) { + if (h_local>0 && v_local>0) heatmap_set(wf_heatmap,v_global+1,h_global+1,score); + } else { + if (h_local>0 && v_local>0) heatmap_set(wf_heatmap,v_global-1,h_global-1,score); + } + // Simulate extension + if (extend) { + while (v_local < pattern_length && + h_local < text_length && + pattern[v_local] == text[h_local]) { + if (reverse) { + v_global--; h_global--; + } else { + v_global++; h_global++; + } + v_local++; h_local++; + if (reverse) { + heatmap_set(wf_heatmap,v_global+1,h_global+1,score); + } else { + heatmap_set(wf_heatmap,v_global-1,h_global-1,score); + } + } + } + } +} +void wavefront_plot( + wavefront_aligner_t* const wf_aligner, + const int score, + const int align_level) { + //if (wf_aligner->align_mode == wf_align_biwfa_breakpoint_forward) return; + // Check plotting enabled wrt align-level + if (wf_aligner->align_mode == wf_align_biwfa_breakpoint_forward || + wf_aligner->align_mode == wf_align_biwfa_breakpoint_reverse) { + if (align_level != wf_aligner->plot->attributes.align_level) return; + } + if (wf_aligner->align_mode == wf_align_biwfa_subsidiary && + wf_aligner->plot->attributes.align_level != -1) return; + // Parameters + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const int score_mod = (wf_components->memory_modular) ? score%wf_components->max_score_scope : score; + // Plot wavefront components + wavefront_plot_component(wf_aligner, + wf_components->mwavefronts[score_mod], + score,wf_aligner->plot->m_heatmap,true); + if (distance_metric < gap_affine) return; + // Gap-affine + wavefront_plot_component(wf_aligner, + wf_components->i1wavefronts[score_mod], + score,wf_aligner->plot->i1_heatmap,false); + wavefront_plot_component(wf_aligner, + wf_components->d1wavefronts[score_mod], + score,wf_aligner->plot->d1_heatmap,false); + if (distance_metric == gap_affine) return; + // Gap-affine-2p + wavefront_plot_component(wf_aligner, + wf_components->i2wavefronts[score_mod], + score,wf_aligner->plot->i2_heatmap,false); + wavefront_plot_component(wf_aligner, + wf_components->d2wavefronts[score_mod], + score,wf_aligner->plot->d2_heatmap,false); +} +/* + * Display + */ +void wavefront_plot_print_cigar( + FILE* const stream, + cigar_t* const cigar, + const char target_operation) { + int i, h=0, v=0, count=0; + for (i=cigar->begin_offset;iend_offset;++i) { + // Check operation + const char operation = cigar->operations[i]; + switch (operation) { + case 'M': case 'X': ++h; ++v; break; + case 'I': ++h; break; + case 'D': ++v; break; + default: break; + } + // Print point + if (operation == target_operation && h>0 && v>0) { + if (count++ > 0) fprintf(stream,";"); + fprintf(stream,"%d,%d",h-1,v-1); + } + } +} +void wavefront_plot_print( + FILE* const stream, + wavefront_aligner_t* const wf_aligner) { + // Parameters + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + wavefront_plot_t* const wf_plot = wf_aligner->plot; + // Metadata + if (wf_aligner->match_funct != NULL) { + fprintf(stream,"# PatternLength 0\n"); + fprintf(stream,"# TextLength 0\n"); + fprintf(stream,"# Pattern -\n"); + fprintf(stream,"# Text -\n"); + } else { + fprintf(stream,"# PatternLength %d\n",wf_aligner->pattern_length); + fprintf(stream,"# Pattern %.*s\n",wf_aligner->pattern_length,wf_aligner->pattern); + fprintf(stream,"# TextLength %d\n",wf_aligner->text_length); + fprintf(stream,"# Text %.*s\n",wf_aligner->text_length,wf_aligner->text); + } + fprintf(stream,"# Penalties "); + wavefront_penalties_print(stream,&wf_aligner->penalties); + fprintf(stream,"\n"); + // Alignment mode + fprintf(stream,"# WFAMode "); + wavefront_aligner_print_mode(stream,wf_aligner); + wavefront_heuristic_t* const wf_heuristic = &wf_aligner->heuristic; + if (wf_heuristic->strategy != wf_heuristic_none) { + wavefront_heuristic_print(stream,wf_heuristic); + } + fprintf(stream,"\n"); + // Wavefront components + fprintf(stream,"# Heatmap M\n"); heatmap_print(stream,wf_plot->m_heatmap); + if (distance_metric == gap_affine) { + fprintf(stream,"# Heatmap I1\n"); heatmap_print(stream,wf_plot->i1_heatmap); + fprintf(stream,"# Heatmap D1\n"); heatmap_print(stream,wf_plot->d1_heatmap); + } + if (distance_metric == gap_affine_2p) { + fprintf(stream,"# Heatmap I2\n"); heatmap_print(stream,wf_plot->i2_heatmap); + fprintf(stream,"# Heatmap D2\n"); heatmap_print(stream,wf_plot->d2_heatmap); + } + // Extend + fprintf(stream,"# Heatmap Extend\n"); heatmap_print(stream,wf_plot->behavior_heatmap); + // CIGAR + if (wf_aligner->alignment_scope == compute_alignment) { + fprintf(stream,"# List CIGAR-M "); + wavefront_plot_print_cigar(stream,wf_aligner->cigar,'M'); + fprintf(stream,"\n"); + fprintf(stream,"# List CIGAR-X "); + wavefront_plot_print_cigar(stream,wf_aligner->cigar,'X'); + fprintf(stream,"\n"); + fprintf(stream,"# List CIGAR-I "); + wavefront_plot_print_cigar(stream,wf_aligner->cigar,'I'); + fprintf(stream,"\n"); + fprintf(stream,"# List CIGAR-D "); + wavefront_plot_print_cigar(stream,wf_aligner->cigar,'D'); + fprintf(stream,"\n"); + } +} diff --git a/src/lib/wfa2/wavefront/wavefront_plot.h b/src/lib/wfa2/wavefront/wavefront_plot.h new file mode 100644 index 000000000..c02a3f1d3 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_plot.h @@ -0,0 +1,101 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: Wavefront alignment module for plot + */ + +#ifndef WAVEFRONT_PLOT_H_ +#define WAVEFRONT_PLOT_H_ + +#include "../utils/commons.h" +#include "../utils/heatmap.h" +#include "../alignment/score_matrix.h" +#include "../wavefront/wavefront_penalties.h" + +// Wavefront ahead definition +typedef struct _wavefront_aligner_t wavefront_aligner_t; + +/* + * Wavefront Display + */ +typedef struct { + bool enabled; // Is plotting enabled + int resolution_points; // Total resolution points + int align_level; // Level of recursion to plot (-1 == final) +} wavefront_plot_attr_t; +typedef struct { + // Configuration + wavefront_plot_attr_t attributes; + distance_metric_t distance_metric; + int min_v; + int max_v; + int min_h; + int max_h; + // Wavefront Heatmaps + heatmap_t* m_heatmap; + heatmap_t* i1_heatmap; + heatmap_t* d1_heatmap; + heatmap_t* i2_heatmap; + heatmap_t* d2_heatmap; + heatmap_t* behavior_heatmap; + // Offsets + int offset_h; + int offset_v; +} wavefront_plot_t; + +/* + * Setup + */ +wavefront_plot_t* wavefront_plot_new( + const distance_metric_t distance_metric, + const int pattern_length, + const int text_length, + wavefront_plot_attr_t* const attributes); +void wavefront_plot_resize( + wavefront_plot_t* const wf_plot, + const int pattern_length, + const int text_length); +void wavefront_plot_delete( + wavefront_plot_t* const wf_plot); + +/* + * Accessors + */ +void wavefront_plot( + wavefront_aligner_t* const wf_aligner, + const int score, + const int align_level); + +/* + * Display + */ +void wavefront_plot_print( + FILE* const stream, + wavefront_aligner_t* const wf_aligner); + +#endif /* WAVEFRONT_PLOT_H_ */ diff --git a/src/lib/wfa2/wavefront/wavefront_slab.c b/src/lib/wfa2/wavefront/wavefront_slab.c new file mode 100644 index 000000000..89a897b51 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_slab.c @@ -0,0 +1,291 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront Slab for fast pre-allocated wavefronts' memory handling + */ + +#include "wavefront_slab.h" + +/* + * Constants + */ +#define WF_SLAB_EXPAND_FACTOR 1.5f +#define WF_SLAB_QUEUES_LENGTH_INIT 100 + +/* + * Setup + */ +wavefront_slab_t* wavefront_slab_new( + const int init_wf_length, + const bool allocate_backtrace, + const wf_slab_mode_t slab_mode, + mm_allocator_t* const mm_allocator) { + // Allocate + wavefront_slab_t* const wavefront_slab = + mm_allocator_alloc(mm_allocator,wavefront_slab_t); + // Attributes + wavefront_slab->allocate_backtrace = allocate_backtrace; + wavefront_slab->slab_mode = slab_mode; + // Wavefront Slabs + wavefront_slab->init_wf_length = init_wf_length; + wavefront_slab->current_wf_length = init_wf_length; + wavefront_slab->wavefronts = vector_new(WF_SLAB_QUEUES_LENGTH_INIT,wavefront_t*); + wavefront_slab->wavefronts_free = vector_new(WF_SLAB_QUEUES_LENGTH_INIT,wavefront_t*); + // Stats + wavefront_slab->memory_used = 0; + // MM + wavefront_slab->mm_allocator = mm_allocator; + // Return + return wavefront_slab; +} +void wavefront_slab_reap_free( + wavefront_slab_t* const wavefront_slab) { + // Parameters + wavefront_t** const wavefronts = vector_get_mem(wavefront_slab->wavefronts,wavefront_t*); + const int num_wavefronts = vector_get_used(wavefront_slab->wavefronts); + mm_allocator_t* const mm_allocator = wavefront_slab->mm_allocator; + // Remove "deallocated" and free wavefronts + int i, valid_idx = 0; + for (i=0;istatus) { + case wavefront_status_deallocated: + mm_allocator_free(mm_allocator,wavefronts[i]); // Delete handler + break; + case wavefront_status_busy: + wavefronts[valid_idx++] = wavefronts[i]; // Valid wavefront + break; + case wavefront_status_free: + wavefront_free(wavefronts[i],mm_allocator); // Free wavefront + wavefront_slab->memory_used -= wavefront_get_size(wavefronts[i]); + mm_allocator_free(mm_allocator,wavefronts[i]); // Delete handler + break; + } + } + vector_set_used(wavefront_slab->wavefronts,valid_idx); + vector_clear(wavefront_slab->wavefronts_free); +} +void wavefront_slab_reap_repurpose( + wavefront_slab_t* const wavefront_slab) { + // Parameters + const int current_wf_length = wavefront_slab->current_wf_length; + wavefront_t** const wavefronts = vector_get_mem(wavefront_slab->wavefronts,wavefront_t*); + const int num_wavefronts = vector_get_used(wavefront_slab->wavefronts); + mm_allocator_t* const mm_allocator = wavefront_slab->mm_allocator; + // Clear free + vector_reserve(wavefront_slab->wavefronts_free,num_wavefronts,false); + wavefront_t** const wavefronts_free = vector_get_mem(wavefront_slab->wavefronts_free,wavefront_t*); + // Remove "deallocated" and repurpose all we can of current wf-length + int i, valid_idx = 0; + for (i=0;istatus) { + case wavefront_status_deallocated: + mm_allocator_free(mm_allocator,wavefronts[i]); // Delete handler + break; + case wavefront_status_busy: + case wavefront_status_free: + if (wavefronts[i]->wf_elements_allocated == current_wf_length) { + wavefronts[i]->status = wavefront_status_free; // Set free + wavefronts[valid_idx] = wavefronts[i]; // Valid wavefront + wavefronts_free[valid_idx] = wavefronts[i]; // Free wavefront + valid_idx++; + } else { + wavefront_free(wavefronts[i],mm_allocator); // Free wavefront + wavefront_slab->memory_used -= wavefront_get_size(wavefronts[i]); + mm_allocator_free(mm_allocator,wavefronts[i]); // Delete handler + } + break; + } + } + vector_set_used(wavefront_slab->wavefronts,valid_idx); + vector_set_used(wavefront_slab->wavefronts_free,valid_idx); +} +void wavefront_slab_reap( + wavefront_slab_t* const wavefront_slab) { + // Back to initial size + wavefront_slab->current_wf_length = wavefront_slab->init_wf_length; + wavefront_slab_reap_repurpose(wavefront_slab); // Repurpose all wavefronts +} +void wavefront_slab_clear( + wavefront_slab_t* const wavefront_slab) { + // Select slab mode + switch (wavefront_slab->slab_mode) { + case wf_slab_reuse: + wavefront_slab_reap_repurpose(wavefront_slab); + break; + case wf_slab_tight: + // Back to initial size + wavefront_slab->current_wf_length = wavefront_slab->init_wf_length; + wavefront_slab_reap_repurpose(wavefront_slab); + break; + } +} +void wavefront_slab_delete( + wavefront_slab_t* const wavefront_slab) { + // Parameters + mm_allocator_t* const mm_allocator = wavefront_slab->mm_allocator; + // Delete free vector + vector_delete(wavefront_slab->wavefronts_free); + // Free wavefronts + wavefront_t** const wavefronts = + vector_get_mem(wavefront_slab->wavefronts,wavefront_t*); + const int num_wavefronts = vector_get_used(wavefront_slab->wavefronts); + int i; + for (i=0;istatus == wavefront_status_deallocated) { + mm_allocator_free(mm_allocator,wavefronts[i]); // Delete handler + } else { + wavefront_free(wavefronts[i],mm_allocator); // Free wavefront + mm_allocator_free(mm_allocator,wavefronts[i]); // Delete handler + } + } + vector_delete(wavefront_slab->wavefronts); + // Handler + mm_allocator_free(wavefront_slab->mm_allocator,wavefront_slab); +} +/* + * Accessors + */ +void wavefront_slab_set_mode( + wavefront_slab_t* const wavefront_slab, + const wf_slab_mode_t slab_mode) { + // Check mode + if (slab_mode != wavefront_slab->slab_mode) { + // Change mode + wavefront_slab->slab_mode = slab_mode; + // Reap + wavefront_slab->current_wf_length = wavefront_slab->init_wf_length; + wavefront_slab_reap_repurpose(wavefront_slab); + } +} +/* + * Slab Allocator + */ +wavefront_t* wavefront_slab_allocate_new( + wavefront_slab_t* const wavefront_slab, + const int wf_length_requested, + const int min_lo, + const int max_hi) { + // Allocate a new wavefront + mm_allocator_t* const mm_allocator = wavefront_slab->mm_allocator; + wavefront_t* const wavefront = mm_allocator_alloc(mm_allocator,wavefront_t); + wavefront_allocate(wavefront,wf_length_requested,wavefront_slab->allocate_backtrace,mm_allocator); + vector_insert(wavefront_slab->wavefronts,wavefront,wavefront_t*); + wavefront_slab->memory_used += wavefront_get_size(wavefront); + // Init wavefront + wavefront->status = wavefront_status_busy; + wavefront_init(wavefront,min_lo,max_hi); + // Return + return wavefront; +} +wavefront_t* wavefront_slab_allocate_free( + wavefront_slab_t* const wavefront_slab, + const int min_lo, + const int max_hi) { + // Parameters + vector_t* const wavefronts_free = wavefront_slab->wavefronts_free; + // Reuse wavefront + wavefront_t* const wavefront = *(vector_get_last_elm(wavefronts_free,wavefront_t*)); + vector_dec_used(wavefronts_free); + // Init wavefront + wavefront->status = wavefront_status_busy; + wavefront_init(wavefront,min_lo,max_hi); + // Return + return wavefront; +} +wavefront_t* wavefront_slab_allocate( + wavefront_slab_t* const wavefront_slab, + const int min_lo, + const int max_hi) { + // Parameters + vector_t* const wavefronts_free = wavefront_slab->wavefronts_free; + const int wf_length = WAVEFRONT_LENGTH(min_lo,max_hi); + // Check slab-mode + if (wavefront_slab->slab_mode == wf_slab_reuse) { + // Check max-length of pre-allocated wavefronts + if (wf_length > wavefront_slab->current_wf_length) { + const int proposed_wf_length = (float)wf_length * WF_SLAB_EXPAND_FACTOR; + wavefront_slab->current_wf_length = proposed_wf_length; // New slab size + wavefront_slab_reap_free(wavefront_slab); // Reap free wavefronts + } + // Check for a free wavefront (pre-allocated in the slab) + if (vector_get_used(wavefronts_free) > 0) { + return wavefront_slab_allocate_free(wavefront_slab,min_lo,max_hi); + } else { + // Allocate a new wavefront + return wavefront_slab_allocate_new(wavefront_slab, + wavefront_slab->current_wf_length,min_lo,max_hi); + } + } else { // wf_slab_tight + if (wf_length <= wavefront_slab->init_wf_length) { + // Check for a free wavefront (pre-allocated in the slab) + if (vector_get_used(wavefronts_free) > 0) { + return wavefront_slab_allocate_free(wavefront_slab,min_lo,max_hi); + } else { + return wavefront_slab_allocate_new(wavefront_slab, + wavefront_slab->init_wf_length,min_lo,max_hi); // Allocate new + } + } else { + return wavefront_slab_allocate_new(wavefront_slab, + wf_length,min_lo,max_hi); // Allocate new + } + } +} +void wavefront_slab_free( + wavefront_slab_t* const wavefront_slab, + wavefront_t* const wavefront) { + // Check reasons to repurpose wavefront (NOTE: Tight-mode never slab_frees()) + // (A) Reuse-mode and wavefront has current wf-length + // (B) Tight-mode and wavefront has init wf-length + const int wf_length = wavefront->wf_elements_allocated; + const bool repurpose_reuse = + (wavefront_slab->slab_mode == wf_slab_reuse) && + (wf_length == wavefront_slab->current_wf_length); + const bool repurpose_tight = + (wavefront_slab->slab_mode == wf_slab_tight) && + (wf_length == wavefront_slab->init_wf_length); + if (repurpose_reuse || repurpose_tight) { + // Return wavefront to slab as free (Good job recycling) + wavefront->status = wavefront_status_free; + vector_insert(wavefront_slab->wavefronts_free,wavefront,wavefront_t*); + } else { + // Delete wavefront + wavefront->status = wavefront_status_deallocated; + wavefront_slab->memory_used -= wavefront_get_size(wavefront); + wavefront_free(wavefront,wavefront_slab->mm_allocator); + } +} +/* + * Utils + */ +uint64_t wavefront_slab_get_size( + wavefront_slab_t* const wavefront_slab) { + return wavefront_slab->memory_used; +} + + + diff --git a/src/lib/wfa2/wavefront/wavefront_slab.h b/src/lib/wfa2/wavefront/wavefront_slab.h new file mode 100644 index 000000000..81681d8af --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_slab.h @@ -0,0 +1,103 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + * DESCRIPTION: WaveFront Slab for fast pre-allocated wavefronts' memory handling + */ + +#ifndef WAVEFRONT_SLAB_H_ +#define WAVEFRONT_SLAB_H_ + +#include "../utils/commons.h" +#include "../utils/vector.h" +#include "../system/mm_allocator.h" +#include "wavefront.h" + +/* + * Memory Manager for Wavefront + */ +typedef enum { + wf_slab_reuse = 1, // Keep all wavefronts (Reap only by demand) + wf_slab_tight = 2, // Reap all if wavefronts are resized +} wf_slab_mode_t; +typedef struct { + // Attributes + bool allocate_backtrace; // WFs require BT-vector + wf_slab_mode_t slab_mode; // Slab strategy + // Wavefront Slabs + int init_wf_length; // Initial wf-elements allocated + int current_wf_length; // Current wf-elements allocated + vector_t* wavefronts; // All wavefronts (wavefront_t*) + vector_t* wavefronts_free; // Free wavefronts (wavefront_t*) + // Stats + uint64_t memory_used; // Memory used (Bytes) + // MM + mm_allocator_t* mm_allocator; // MM-Allocator +} wavefront_slab_t; + +/* + * Setup + */ +wavefront_slab_t* wavefront_slab_new( + const int init_wf_length, + const bool allocate_backtrace, + const wf_slab_mode_t slab_mode, + mm_allocator_t* const mm_allocator); +void wavefront_slab_reap( + wavefront_slab_t* const wavefront_slab); +void wavefront_slab_clear( + wavefront_slab_t* const wavefront_slab); +void wavefront_slab_delete( + wavefront_slab_t* const wavefront_slab); + +/* + * Accessors + */ +void wavefront_slab_set_mode( + wavefront_slab_t* const wavefront_slab, + const wf_slab_mode_t slab_mode); + +/* + * Allocator + */ +wavefront_t* wavefront_slab_allocate( + wavefront_slab_t* const wavefront_slab, + const int min_lo, + const int max_hi); +void wavefront_slab_free( + wavefront_slab_t* const wavefront_slab, + wavefront_t* const wavefront); + +/* + * Utils + */ +uint64_t wavefront_slab_get_size( + wavefront_slab_t* const wavefront_slab); + +#endif /* WAVEFRONT_SLAB_H_ */ + + diff --git a/src/lib/wfa2/wavefront/wavefront_unialign.c b/src/lib/wfa2/wavefront/wavefront_unialign.c new file mode 100644 index 000000000..0b841faba --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_unialign.c @@ -0,0 +1,489 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + */ + +#include "wavefront_unialign.h" +#include "wavefront.h" +#include "wavefront_attributes.h" +#include "wavefront_offset.h" +#include "wavefront_penalties.h" +#include "wavefront_plot.h" +#include "wavefront_slab.h" + +#include "wavefront_components.h" +#include "wavefront_compute.h" +#include "wavefront_compute_affine.h" +#include "wavefront_compute_affine2p.h" +#include "wavefront_compute_edit.h" +#include "wavefront_compute_linear.h" +#include "wavefront_extend.h" +#include "wavefront_backtrace.h" +#include "wavefront_backtrace_buffer.h" + +/* + * Configuration + */ +#define SEQUENCES_PADDING 10 + +/* + * Setup + */ +void wavefront_unialign_status_clear( + wavefront_align_status_t* const align_status) { + align_status->status = WF_STATUS_SUCCESSFUL; + align_status->score = 0; +} +void wavefront_unialigner_system_clear( + wavefront_aligner_t* const wf_aligner) { + // Reset effective limits + wf_aligner->system.max_memory_compact = BUFFER_SIZE_256M; + wf_aligner->system.max_memory_resident = BUFFER_SIZE_256M + BUFFER_SIZE_256M; + switch (wf_aligner->memory_mode) { + case wavefront_memory_med: + wf_aligner->system.max_partial_compacts = 4; + break; + case wavefront_memory_low: + wf_aligner->system.max_partial_compacts = 1; + break; + default: + break; + } + // Profile + timer_reset(&wf_aligner->system.timer); +} +/* + * Resize + */ +void wavefront_unialign_resize( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + const bool reverse_sequences) { + // Configure sequences and status + wf_aligner->pattern_length = pattern_length; + wf_aligner->text_length = text_length; + if (wf_aligner->match_funct == NULL) { + if (wf_aligner->sequences != NULL) strings_padded_delete(wf_aligner->sequences); + wf_aligner->sequences = strings_padded_new_rhomb( + pattern,pattern_length,text,text_length, + SEQUENCES_PADDING,reverse_sequences, + wf_aligner->mm_allocator); + wf_aligner->pattern = wf_aligner->sequences->pattern_padded; + wf_aligner->text = wf_aligner->sequences->text_padded; + } else { + wf_aligner->sequences = NULL; + wf_aligner->pattern = NULL; + wf_aligner->text = NULL; + } + wavefront_unialign_status_clear(&wf_aligner->align_status); + // Heuristics clear + wavefront_heuristic_clear(&wf_aligner->heuristic); + // Wavefront components + wavefront_components_resize(&wf_aligner->wf_components, + pattern_length,text_length,&wf_aligner->penalties); + // CIGAR + if (wf_aligner->alignment_scope == compute_alignment) { + cigar_resize(wf_aligner->cigar,2*(pattern_length+text_length)); + } + // Slab + wavefront_slab_clear(wf_aligner->wavefront_slab); + // System + wavefront_unialigner_system_clear(wf_aligner); +} +/* + * Initialize alignment + */ +void wavefront_unialign_initialize_wavefront_m( + wavefront_aligner_t* const wf_aligner, + const int pattern_length, + const int text_length) { + // Parameters + wavefront_slab_t* const wavefront_slab = wf_aligner->wavefront_slab; + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + wavefront_penalties_t* const penalties = &wf_aligner->penalties; + alignment_form_t* const form = &wf_aligner->alignment_form; + // Consider ends-free + const int hi = (penalties->match==0) ? form->text_begin_free : 0; + const int lo = (penalties->match==0) ? -form->pattern_begin_free : 0; + // Compute dimensions + int effective_lo, effective_hi; + wavefront_compute_limits_output(wf_aligner,lo,hi,&effective_lo,&effective_hi); + // Initialize end2end (wavefront zero) + wf_components->mwavefronts[0] = wavefront_slab_allocate(wavefront_slab,effective_lo,effective_hi); + wf_components->mwavefronts[0]->offsets[0] = 0; + wf_components->mwavefronts[0]->lo = lo; + wf_components->mwavefronts[0]->hi = hi; + // Store initial BT-piggypack element + if (wf_components->bt_piggyback) { + const bt_block_idx_t block_idx = wf_backtrace_buffer_init_block(wf_components->bt_buffer,0,0); + wf_components->mwavefronts[0]->bt_pcigar[0] = 0; + wf_components->mwavefronts[0]->bt_prev[0] = block_idx; + } + // Initialize ends-free + if (form->span == alignment_endsfree && penalties->match == 0) { + // Text begin-free + const int text_begin_free = form->text_begin_free; + int h; + for (h=1;h<=text_begin_free;++h) { + const int k = DPMATRIX_DIAGONAL(h,0); + wf_components->mwavefronts[0]->offsets[k] = DPMATRIX_OFFSET(h,0); + if (wf_components->bt_piggyback) { + const bt_block_idx_t block_idx = wf_backtrace_buffer_init_block(wf_components->bt_buffer,0,h); + wf_components->mwavefronts[0]->bt_pcigar[k] = 0; + wf_components->mwavefronts[0]->bt_prev[k] = block_idx; + } + } + // Pattern begin-free + const int pattern_begin_free = form->pattern_begin_free; + int v; + for (v=1;v<=pattern_begin_free;++v) { + const int k = DPMATRIX_DIAGONAL(0,v); + wf_components->mwavefronts[0]->offsets[k] = DPMATRIX_OFFSET(0,v); + if (wf_components->bt_piggyback) { + const bt_block_idx_t block_idx = wf_backtrace_buffer_init_block(wf_components->bt_buffer,v,0); + wf_components->mwavefronts[0]->bt_pcigar[k] = 0; + wf_components->mwavefronts[0]->bt_prev[k] = block_idx; + } + } + } + // Nullify unused WFs + if (distance_metric <= gap_linear) return; + wf_components->d1wavefronts[0] = NULL; + wf_components->i1wavefronts[0] = NULL; + if (distance_metric==gap_affine) return; + wf_components->d2wavefronts[0] = NULL; + wf_components->i2wavefronts[0] = NULL; +} +void wavefront_unialign_initialize_wavefronts( + wavefront_aligner_t* const wf_aligner, + const int pattern_length, + const int text_length) { + // Parameters + wavefront_slab_t* const wavefront_slab = wf_aligner->wavefront_slab; + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const distance_metric_t distance_metric = wf_aligner->penalties.distance_metric; + // Init wavefronts + if (wf_aligner->component_begin == affine2p_matrix_M) { + // Initialize + wavefront_unialign_initialize_wavefront_m(wf_aligner,pattern_length,text_length); + // Nullify unused WFs + if (distance_metric <= gap_linear) return; + wf_components->i1wavefronts[0] = NULL; + wf_components->d1wavefronts[0] = NULL; + if (distance_metric==gap_affine) return; + wf_components->i2wavefronts[0] = NULL; + wf_components->d2wavefronts[0] = NULL; + } else { + // Compute dimensions + int effective_lo, effective_hi; // Effective lo/hi + wavefront_compute_limits_output(wf_aligner,0,0,&effective_lo,&effective_hi); + wavefront_t* const wavefront = wavefront_slab_allocate(wavefront_slab,effective_lo,effective_hi); + // Initialize + switch (wf_aligner->component_begin) { + case affine2p_matrix_I1: + wf_components->mwavefronts[0] = NULL; + wf_components->i1wavefronts[0] = wavefront; + wf_components->i1wavefronts[0]->offsets[0] = 0; + wf_components->i1wavefronts[0]->lo = 0; + wf_components->i1wavefronts[0]->hi = 0; + wf_components->d1wavefronts[0] = NULL; + // Nullify unused WFs + if (distance_metric==gap_affine) return; + wf_components->i2wavefronts[0] = NULL; + wf_components->d2wavefronts[0] = NULL; + break; + case affine2p_matrix_I2: + wf_components->mwavefronts[0] = NULL; + wf_components->i1wavefronts[0] = NULL; + wf_components->d1wavefronts[0] = NULL; + wf_components->i2wavefronts[0] = wavefront; + wf_components->i2wavefronts[0]->offsets[0] = 0; + wf_components->i2wavefronts[0]->lo = 0; + wf_components->i2wavefronts[0]->hi = 0; + wf_components->d2wavefronts[0] = NULL; + break; + case affine2p_matrix_D1: + wf_components->mwavefronts[0] = NULL; + wf_components->i1wavefronts[0] = NULL; + wf_components->d1wavefronts[0] = wavefront; + wf_components->d1wavefronts[0]->offsets[0] = 0; + wf_components->d1wavefronts[0]->lo = 0; + wf_components->d1wavefronts[0]->hi = 0; + // Nullify unused WFs + if (distance_metric==gap_affine) return; + wf_components->i2wavefronts[0] = NULL; + wf_components->d2wavefronts[0] = NULL; + break; + case affine2p_matrix_D2: + wf_components->mwavefronts[0] = NULL; + wf_components->i1wavefronts[0] = NULL; + wf_components->d1wavefronts[0] = NULL; + wf_components->i2wavefronts[0] = NULL; + wf_components->d2wavefronts[0] = wavefront; + wf_components->d2wavefronts[0]->offsets[0] = 0; + wf_components->d2wavefronts[0]->lo = 0; + wf_components->d2wavefronts[0]->hi = 0; + break; + default: + break; + } + } +} +void wavefront_unialign_init( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + const affine2p_matrix_type component_begin, + const affine2p_matrix_type component_end) { + // Parameters + wavefront_align_status_t* const align_status = &wf_aligner->align_status; + // Resize wavefront aligner + wavefront_unialign_resize(wf_aligner,pattern,pattern_length,text,text_length,false); + // Configure WF-compute function + switch (wf_aligner->penalties.distance_metric) { + case indel: + case edit: + align_status->wf_align_compute = &wavefront_compute_edit; + break; + case gap_linear: + align_status->wf_align_compute = &wavefront_compute_linear; + break; + case gap_affine: + align_status->wf_align_compute = &wavefront_compute_affine; + break; + case gap_affine_2p: + align_status->wf_align_compute = &wavefront_compute_affine2p; + break; + default: + fprintf(stderr,"[WFA] Distance function not implemented\n"); + exit(1); + break; + } + // Configure WF-extend function + const bool end2end = (wf_aligner->alignment_form.span == alignment_end2end); + if (wf_aligner->match_funct != NULL) { + align_status->wf_align_extend = &wavefront_extend_custom; + } else if (end2end) { + align_status->wf_align_extend = &wavefront_extend_end2end; + } else { + align_status->wf_align_extend = &wavefront_extend_endsfree; + } + // Initialize wavefront + wf_aligner->alignment_end_pos.score = -1; // Not aligned + wf_aligner->alignment_end_pos.k = DPMATRIX_DIAGONAL_NULL; + wf_aligner->component_begin = component_begin; + wf_aligner->component_end = component_end; + wavefront_unialign_initialize_wavefronts(wf_aligner,pattern_length,text_length); + // Plot (WF_0) + if (wf_aligner->plot != NULL) wavefront_plot(wf_aligner,0,0); +} +/* + * Limits + */ +bool wavefront_unialign_reached_limits( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Check alignment-score limit + if (score >= wf_aligner->system.max_alignment_score) { + wf_aligner->cigar->score = wf_aligner->system.max_alignment_score; + wf_aligner->align_status.status = WF_STATUS_MAX_SCORE_REACHED; + wf_aligner->align_status.score = score; + return true; // Stop + } + // Global probing interval + alignment_system_t* const system = &wf_aligner->system; + if (score % system->probe_interval_global != 0) return false; // Continue + if (system->verbose >= 1) { + wavefront_unialign_print_status(stderr,wf_aligner,score); // DEBUG + } + // BT-Buffer + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + if (wf_components->bt_buffer!=NULL && (score%system->probe_interval_compact)==0) { + uint64_t bt_memory = wf_backtrace_buffer_get_size_used(wf_components->bt_buffer); + // Check BT-buffer memory + if (bt_memory > system->max_memory_compact) { + // Compact BT-buffer + wavefront_components_compact_bt_buffer(wf_components,score,wf_aligner->system.verbose); + // Set new buffer limit + bt_memory = wf_backtrace_buffer_get_size_used(wf_components->bt_buffer); + uint64_t proposed_mem = (double)bt_memory * TELESCOPIC_FACTOR; + if (system->max_memory_compact < proposed_mem && proposed_mem < system->max_memory_abort) { + proposed_mem = system->max_memory_compact; + } + // Reset (if maximum compacts has been performed) + if (wf_components->bt_buffer->num_compactions >= system->max_partial_compacts) { + wf_backtrace_buffer_reset_compaction(wf_components->bt_buffer); + } + } + } + // Check overall memory used + const uint64_t wf_memory_used = wavefront_aligner_get_size(wf_aligner); + if (wf_memory_used > system->max_memory_abort) { + wf_aligner->align_status.status = WF_STATUS_OOM; + wf_aligner->align_status.score = score; + return true; // Stop + } + // Otherwise continue + return false; +} +/* + * Terminate alignment (backtrace) + */ +void wavefront_unialign_terminate( + wavefront_aligner_t* const wf_aligner, + const int score) { + // Parameters + const int pattern_length = wf_aligner->pattern_length; + const int text_length = wf_aligner->text_length; + // Retrieve alignment + if (wf_aligner->alignment_scope == compute_score) { + cigar_clear(wf_aligner->cigar); + wf_aligner->cigar->score = + wavefront_compute_classic_score(wf_aligner,pattern_length,text_length,score); + } else { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + const int alignment_end_k = wf_aligner->alignment_end_pos.k; + const wf_offset_t alignment_end_offset = wf_aligner->alignment_end_pos.offset; + if (wf_components->bt_piggyback) { + // Fetch wavefront + const bool memory_modular = wf_aligner->wf_components.memory_modular; + const int max_score_scope = wf_aligner->wf_components.max_score_scope; + const int score_mod = (memory_modular) ? score % max_score_scope : score; + wavefront_t* const mwavefront = wf_components->mwavefronts[score_mod]; + // Backtrace alignment from buffer (unpacking pcigar) + wavefront_backtrace_pcigar( + wf_aligner,alignment_end_k,alignment_end_offset, + mwavefront->bt_pcigar[alignment_end_k], + mwavefront->bt_prev[alignment_end_k]); + } else { + // Backtrace alignment + if (wf_aligner->penalties.distance_metric <= gap_linear) { + wavefront_backtrace_linear(wf_aligner, + score,alignment_end_k,alignment_end_offset); + } else { + wavefront_backtrace_affine(wf_aligner, + wf_aligner->component_begin,wf_aligner->component_end, + score,alignment_end_k,alignment_end_offset); + } + } + // Set score & finish + wf_aligner->cigar->score = + wavefront_compute_classic_score(wf_aligner,pattern_length,text_length,score); + } + // Set successful + wf_aligner->align_status.status = WF_STATUS_SUCCESSFUL; +} +/* + * Classic WF-Alignment (Unidirectional) + */ +int wavefront_unialign( + wavefront_aligner_t* const wf_aligner) { + // Parameters + wavefront_align_status_t* const align_status = &wf_aligner->align_status; + void (*wf_align_compute)(wavefront_aligner_t* const,const int) = align_status->wf_align_compute; + int (*wf_align_extend)(wavefront_aligner_t* const,const int) = align_status->wf_align_extend; + // Compute wavefronts of increasing score + align_status->num_null_steps = 0; + int score = align_status->score; + while (true) { + // Exact extend s-wavefront + const int finished = (*wf_align_extend)(wf_aligner,score); + if (finished) { + // DEBUG + // wavefront_aligner_print(stderr,wf_aligner,0,score,7,0); + if (align_status->status == WF_STATUS_END_REACHED) { + wavefront_unialign_terminate(wf_aligner,score); + } + return align_status->status; + } + // Compute (s+1)-wavefront + ++score; + (*wf_align_compute)(wf_aligner,score); + // Probe limits + if (wavefront_unialign_reached_limits(wf_aligner,score)) return align_status->status; + // Plot + if (wf_aligner->plot != NULL) wavefront_plot(wf_aligner,score,0); + // DEBUG + //wavefront_aligner_print(stderr,wf_aligner,score,score,7,0); + } + // Return OK + align_status->score = score; + align_status->status = WF_STATUS_SUCCESSFUL; + return WF_STATUS_SUCCESSFUL; +} +/* + * Display + */ +void wavefront_unialign_print_status( + FILE* const stream, + wavefront_aligner_t* const wf_aligner, + const int score) { + // Parameters + wavefront_components_t* const wf_components = &wf_aligner->wf_components; + // Approximate progress + const int dist_total = MAX(wf_aligner->text_length,wf_aligner->pattern_length); + int s = (wf_components->memory_modular) ? score%wf_components->max_score_scope : score; + wavefront_t* wavefront = wf_components->mwavefronts[s]; + if (wavefront==NULL && s>0) { + s = (wf_components->memory_modular) ? (score-1)%wf_components->max_score_scope : (score-1); + wavefront = wf_components->mwavefronts[s]; + } + int dist_max = -1, wf_len = -1, k; + if (wavefront!=NULL) { + wf_offset_t* const offsets = wavefront->offsets; + for (k=wavefront->lo;k<=wavefront->hi;++k) { + const int dist = MAX(WAVEFRONT_V(k,offsets[k]),WAVEFRONT_H(k,offsets[k])); + dist_max = MAX(dist_max,dist); + } + wf_len = wavefront->hi-wavefront->lo+1; + } + // Memory used + const uint64_t slab_size = wavefront_slab_get_size(wf_aligner->wavefront_slab); + const uint64_t bt_buffer_used = (wf_components->bt_buffer) ? + wf_backtrace_buffer_get_size_used(wf_components->bt_buffer) : 0; + // Progress + const float aligned_progress = (dist_max>=0) ? (100.0f*(float)dist_max/(float)dist_total) : -1.0f; + const float million_offsets = (wf_len>=0) ? (float)wf_len/1000000.0f : -1.0f; + // Print one-line status + fprintf(stream,"["); + wavefront_aligner_print_type(stream,wf_aligner); + fprintf(stream, + "] SequenceLength=(%d,%d) Score %d (~ %2.3f%% aligned). " + "MemoryUsed(WF-Slab,BT-buffer)=(%lu MB,%lu MB). " + "Wavefronts ~ %2.3f Moffsets\n", + wf_aligner->pattern_length,wf_aligner->text_length,score,aligned_progress, + CONVERT_B_TO_MB(slab_size),CONVERT_B_TO_MB(bt_buffer_used),million_offsets); +} + diff --git a/src/lib/wfa2/wavefront/wavefront_unialign.h b/src/lib/wfa2/wavefront/wavefront_unialign.h new file mode 100644 index 000000000..3055755f8 --- /dev/null +++ b/src/lib/wfa2/wavefront/wavefront_unialign.h @@ -0,0 +1,79 @@ +/* + * The MIT License + * + * Wavefront Alignment Algorithms + * Copyright (c) 2017 by Santiago Marco-Sola + * + * This file is part of Wavefront Alignment Algorithms. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PROJECT: Wavefront Alignment Algorithms + * AUTHOR(S): Santiago Marco-Sola + */ + +#ifndef WAVEFRONT_UNIALIGN_H_ +#define WAVEFRONT_UNIALIGN_H_ + +#include "../utils/commons.h" +#include "wavefront_aligner.h" + +/* + * Resize + */ +void wavefront_unialign_resize( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + const bool reverse_sequences); + +/* + * Initialize alignment + */ +void wavefront_unialign_initialize_wavefronts( + wavefront_aligner_t* const wf_aligner, + const int pattern_length, + const int text_length); +void wavefront_unialign_init( + wavefront_aligner_t* const wf_aligner, + const char* const pattern, + const int pattern_length, + const char* const text, + const int text_length, + const affine2p_matrix_type component_begin, + const affine2p_matrix_type component_end); + +/* + * Classic WF-Alignment (Unidirectional) + */ +int wavefront_unialign( + wavefront_aligner_t* const wf_aligner); + +/* + * Display + */ +void wavefront_unialign_print_status( + FILE* const stream, + wavefront_aligner_t* const wf_aligner, + const int current_score); + +#endif /* WAVEFRONT_UNIALIGN_H_ */ + diff --git a/src/masking/def.h b/src/masking/def.h index 4d595ad06..e3a22fc1b 100644 --- a/src/masking/def.h +++ b/src/masking/def.h @@ -1,3 +1,28 @@ #pragma once -enum struct MaskingAlgo { NONE = 0, TANTAN = 1, SEG = 2, MOTIF = 4 }; \ No newline at end of file +#include +#include "../basic/value.h" +#include "../util/enum.h" + +enum struct MaskingAlgo { NONE = 0, TANTAN = 1, SEG = 2, MOTIF = 4 }; + +DEFINE_ENUM_FLAG_OPERATORS(MaskingAlgo) + +namespace Mask { + +struct Ranges : public std::deque> { + void push_back(Loc begin, Loc end) { + if (empty() || begin > back().second) + emplace_back(begin, end); + else + back().second = end; + } + void push_front(Loc loc) { + if (!empty() && front().first == loc + 1) + front().first = loc; + else + emplace_front(loc, loc + 1); + } +}; + +} \ No newline at end of file diff --git a/src/masking/masking.cpp b/src/masking/masking.cpp index 82cc8af69..1adc68904 100644 --- a/src/masking/masking.cpp +++ b/src/masking/masking.cpp @@ -27,7 +27,7 @@ along with this program. If not, see . #include #include "masking.h" #include "../lib/tantan/LambdaCalculator.hh" -#include "../util/tantan.h" +#include "tantan.h" #include "../lib/blast/blast_filter.h" #include "../data/sequence_set.h" #include "../basic/config.h" @@ -41,7 +41,8 @@ using std::atomic_size_t; const EMap EnumTraits::to_string{ {MaskingAlgo::NONE, "None"}, {MaskingAlgo::SEG, "SEG"}, {MaskingAlgo::TANTAN, "tantan"} }; const SEMap EnumTraits::from_string{ - {"0", MaskingAlgo::NONE} + { "0", MaskingAlgo::NONE }, + { "tantan", MaskingAlgo::TANTAN } }; const SEMap EnumTraits::from_string{ {"0", {MaskingMode::NONE, false}}, @@ -82,7 +83,7 @@ size_t MaskingTable::masked_letters() const { return masked_letters_; } -void MaskingTable::add(const size_t block_id, const int begin, const int end, Letter* seq) { +void MaskingTable::add(const size_t block_id, const Loc begin, const Loc end, Letter* seq) { { std::lock_guard lock(mtx_); entry_.emplace_back(block_id, begin); @@ -115,16 +116,11 @@ void MaskingTable::apply(SequenceSet& seqs) const { static size_t mask_motifs(Letter* seq, const size_t len, const size_t block_id, MaskingTable& table) { if (len < MOTIF_LEN) return 0; - vector> pos; + Mask::Ranges pos; KmerIterator it(Sequence(seq, len)); while (it.good()) { - if (motif_table.find(*it) != motif_table.end()) { - const ptrdiff_t p = it - seq; - if (!pos.empty() && p <= pos.back().second) - pos.back().second = p + MOTIF_LEN; - else - pos.emplace_back(p, p + MOTIF_LEN); - } + if (motif_table.find(*it) != motif_table.end()) + pos.push_back(it - seq, it - seq + MOTIF_LEN); ++it; } const ptrdiff_t n = std::accumulate(pos.cbegin(), pos.cend(), (ptrdiff_t)0, [](const ptrdiff_t s, const pair& r) { return s + r.second - r.first; }); @@ -134,7 +130,7 @@ static size_t mask_motifs(Letter* seq, const size_t len, const size_t block_id, for (auto i = pos.cbegin(); i != pos.cend(); ++i) if (i->second - i->first <= config.max_motif_len) - table.add(block_id, (int)i->first, (int)i->second, seq); + table.add(block_id, i->first, i->second, seq); return n; } @@ -171,11 +167,17 @@ Masking::~Masking() { size_t Masking::operator()(Letter *seq, size_t len, MaskingAlgo algo, const size_t block_id, MaskingTable* table) const { size_t n = 0; - if(flag_any(algo, MaskingAlgo::TANTAN)) - Util::tantan::mask(seq, (int)len, (const float**)probMatrixPointersf_, 0.005f, 0.05f, 1.0f / 0.9f, (float)config.tantan_minMaskProb, mask_table_x_); + if (flag_any(algo, MaskingAlgo::TANTAN)) { + Mask::Ranges r = Util::tantan::mask(seq, (int)len, (const float**)probMatrixPointersf_, 0.005f, 0.05f, 1.0f / 0.9f, (float)config.tantan_minMaskProb, table ? nullptr : mask_table_x_); + if (table) + for (auto i : r) { + table->add(block_id, i.first, i.second, seq); + n += i.second - i.first; + } + } if (flag_any(algo, MaskingAlgo::SEG)) { BlastSeqLoc* seg_locs; - SeqBufferSeg((uint8_t*)seq, len, 0u, blast_seg_, &seg_locs); + SeqBufferSeg((uint8_t*)seq, (uint32_t)len, 0u, blast_seg_, &seg_locs); unsigned nMasked = 0; if (seg_locs) { @@ -215,9 +217,10 @@ void Masking::remove_bit_mask(Letter *seq, size_t len) const seq[i] &= ~bit_mask; } -void mask_worker(atomic_size_t *next, SequenceSet *seqs, const Masking *masking, bool hard_mask, const MaskingAlgo algo, MaskingTable* table, atomic_size_t* count) +void mask_worker(atomic *next, SequenceSet *seqs, const Masking *masking, bool hard_mask, const MaskingAlgo algo, MaskingTable* table, atomic_size_t* count) { - size_t i, n = 0; + BlockId i; + size_t n = 0; while ((i = (*next)++) < seqs->size()) { seqs->convert_to_std_alph(i); if (hard_mask) @@ -235,8 +238,9 @@ size_t mask_seqs(SequenceSet &seqs, const Masking &masking, bool hard_mask, cons if (flag_any(algo, MaskingAlgo::MOTIF) && !table) throw std::runtime_error("Motif masking requires masking table."); vector threads; - atomic_size_t next(0), count(0); - for (size_t i = 0; i < config.threads_; ++i) + atomic next(0); + atomic_size_t count(0); + for (int i = 0; i < config.threads_; ++i) threads.emplace_back(mask_worker, &next, &seqs, &masking, hard_mask, algo, table, &count); for (auto &t : threads) t.join(); diff --git a/src/masking/masking.h b/src/masking/masking.h index a9960049a..0010834a1 100644 --- a/src/masking/masking.h +++ b/src/masking/masking.h @@ -30,6 +30,7 @@ along with this program. If not, see . #include "../data/string_set.h" #include "../util/enum.h" #include "def.h" +#include "../util/kmer/kmer.h" struct MaskingTable; struct SequenceSet; @@ -68,6 +69,7 @@ enum class MaskingMode { NONE, TANTAN, BLAST_SEG }; template<> struct EnumTraits { static const SEMap from_string; + static const EMap to_string; }; struct MaskingTable { @@ -80,6 +82,9 @@ struct MaskingTable { void apply(SequenceSet& seqs) const; bool blank() const; size_t masked_letters() const; + int64_t mem_size() const { + return entry_.size() * sizeof(Entry) + seqs_.mem_size(); + } private: @@ -97,70 +102,5 @@ struct MaskingTable { }; -template -struct Kmer { - Kmer(): - code(0) - { } - Kmer(const char* s) : - code(0) - { - assert(strlen(s) == K); - for (size_t i = 0; i < K; ++i) - code = (code << 5) | (uint64_t)amino_acid_traits.from_char(*s++); - } - operator uint64_t() const { - return code; - } - struct Hash { - size_t operator()(const Kmer k) const { - return std::hash()(k.code); - } - }; - uint64_t code; -}; - -template -struct KmerIterator { - KmerIterator(const Sequence& seq) : - ptr_(seq.data() - 1), - end_(seq.end()) - { - inc(0); - } - Kmer operator*() const { - return kmer_; - } - bool good() const { - return ptr_ < end_; - } - KmerIterator& operator++() { - inc(K - 1); - return *this; - } - ptrdiff_t operator-(const Letter* ptr) { - return ptr_ + 1 - K - ptr; - } -private: - static const uint64_t BITS = 5; - void inc(size_t n) { - do { - ++ptr_; - if (ptr_ == end_) - return; - const uint64_t l = letter_mask(*ptr_); - if (l < TRUE_AA) { - kmer_.code = (kmer_.code << BITS) | l; - ++n; - } - else - n = 0; - } while (n < K); - kmer_.code &= (uint64_t(1) << (BITS * K)) - 1; - } - const Letter* ptr_, *const end_; - Kmer kmer_; -}; - const size_t MOTIF_LEN = 8; extern const std::unordered_set, Kmer::Hash> motif_table; \ No newline at end of file diff --git a/src/masking/motifs.cpp b/src/masking/motifs.cpp index e89003a1e..b1e7edc8d 100644 --- a/src/masking/motifs.cpp +++ b/src/masking/motifs.cpp @@ -1000,4 +1000,7005 @@ const std::unordered_set, Kmer::Hash> motif_table = { "IPSVNNET", "ALNATIEA", "PLTEEAEL", -"KVRQYDQV" }; \ No newline at end of file +"KVRQYDQV", // alt +/*"SVLACYNG", +"FSVLACYN", +"CYNGSPSG", +"YNGSPSGV", +"TFSVLACY", +"VLACYNGS", +"LACYNGSP", +"ACYNGSPS", +"NGSPSGVY", +"GSPSGVYQ", +"MTARTVYD", +"ILTSLLVL", +"GQTFSVLA", +"QTFSVLAC", +"ILMTARTV", +"LMTARTVY", +"LILMTART", +"PGQTFSVL", +"QPGQTFSV", +"LLILMTAR", +"VLLILMTA", +"SPSGVYQC", +"PSGVYQCA", +"SGVYQCAM", +"HEIAWYTE", +"EIAWYTER", +"IAWYTERS", +"EHEIAWYT", +"TARTVYDD", +"AWYTERSE", +"NEKQEILG", +"ARTVYDDG", +"EKQEILGT", +"GVYQCAMR", +"SNEKQEIL", +"ISNEKQEI", +"KQEILGTV", +"IISNEKQE", +"QEILGTVS", +"EILGTVSW", +"AFLCLFLL", +"SIISNEKQ", +"FLCLFLLP", +"LCLFLLPS", +"RTVYDDGA", +"PSIISNEK", +"ILPSIISN", +"YILPSIIS", +"LPSIISNE", +"HAFLCLFL", +"ILGTVSWN", +"VMYASAVV", +"FGDSVEEV", +"CLFLLPSL", +"LFLLPSLA", +"TVYDDGAR", +"LTSLLVLV", +"ASAVVLLI", +"SEKSYELQ", +"YASAVVLL", +"TERSEKSY", +"KHAFLCLF", +"RSEKSYEL", +"LGTVSWNL", +"HKHAFLCL", +"ERSEKSYE", +"MYASAVVL", +"GFGDSVEE", +"VVLLILMT", +"YTERSEKS", +"GDSVEEVL", +"AVVLLILM", +"WYTERSEK", +"DSVEEVLS", +"SLLVLVQS", +"TSLLVLVQ", +"CVMYASAV", +"SAVVLLIL", +"LLVLVQST", +"LVLVQSTQ", +"LKDCVMYA", +"RGFGDSVE", +"KLKDCVMY", +"DCVMYASA", +"KHKHAFLC", +"KDCVMYAS", +"VLVQSTQW", +"QSTQWSLF", +"LVQSTQWS", +"VKHKHAFL", +"FVKHKHAF", +"GTVSWNLR", +"FYILPSII", +"TWLDMVDT", +"MTWLDMVD", +"VLSEARQH", +"TVSWNLRE", +"VRGFGDSV", +"VQSTQWSL", +"MFVKHKHA", +"LVRGFGDS", +"MMFVKHKH", +"WLDMVDTS", +"TQWSLFFF", +"SVEEVLSE", +"DIDITFLK", +"KALRKVPT", +"IDITFLKK", +"EVLSEARQ", +"YKVYYGNA", +"AMMFVKHK", +"VYDDGARR", +"EEVLSEAR", +"VAYFNMVY", +"CCREHEHE", +"STQWSLFF", +"KVYYGNAL", +"LSEARQHL", +"VEEVLSEA", +"EKSYELQT", +"VSWNLREM", +"VYQCAMRP", +"YNKVENMT", +"REHEHEIA", +"YDDGARRV", +"IMTWLDMV", +"LTYNKVEN", +"ALRKVPTD", +"TYNKVENM", +"LRKVPTDN", +"CREHEHEI", +"SWNLREML", +"SDIDITFL", +"LDMVDTSL", +"EHEHEIAW", +"CKSAFYIL", +"IKVFTTVD", +"KSAFYILP", +"MLTYNKVE", +"SAFYILPS", +"VKATCEFC", +"KSYELQTP", +"HEHEIAWY", +"KATCEFCG", +"AFYILPSI", +"LAKALRKV", +"YFNMVYMP", +"RKVPTDNY", +"RIMTWLDM", +"NKVENMTP", +"EKKKLDGF", +"SAFAMMFV", +"KVFTTVDN", +"FVKATCEF", +"AFAMMFVK", +"YCCREHEH", +"AYFNMVYM", +"MSAFAMMF", +"MRIMTWLD", +"SYELQTPF", +"YELQTPFE", +"KKKLDGFM", +"WVMRIMTW", +"SWVMRIMT", +"FAMMFVKH", +"KCKSAFYI", +"KKLDGFMG", +"GAGGHSYG", +"DITFLKKD", +"ATCEFCGT", +"VMRIMTWL", +"LQTPFEIK", +"ELQTPFEI", +"QTPFEIKL", +"FNMVYMPA", +"TCEFCGTE", +"DDGARRVW", +"AKALRKVP", +"CEFCGTEN", +"TPFEIKLA", +"NMVYMPAS", +"AGGHSYGA", +"DFVKATCE", +"KLDGFMGR", +"GDFVKATC", +"TTEMLAKA", +"EFCGTENL", +"EARQHLKD", +"ASWVMRIM", +"YMPASWVM", +"MVYMPASW", +"PASWVMRI", +"MLAKALRK", +"IAMSAFAM", +"DGARRVWT", +"KKCKSAFY", +"TEMLAKAL", +"VYMPASWV", +"SEARQHLK", +"EMLAKALR", +"ARQHLKDG", +"KGAGGHSY", +"VYCCREHE", +"LAWLYAAV", +"APYIVGDV", +"AMSAFAMM", +"MPASWVMR", +"HSYGADLK", +"SYGADLKS", +"RRVWTLMN", +"GGHSYGAD", +"GHSYGADL", +"RVWTLMNV", +"TFLKKDAP", +"ITFLKKDA", +"PYIVGDVV", +"FEIKLAKK", +"LKDGTCGL", +"PFEIKLAK", +"RQHLKDGT", +"LKKCKSAF", +"KDGTCGLV", +"EIKLAKKF", +"IKLAKKFD", +"TCGLVEVE", +"HLKDGTCG", +"GVYCCREH", +"QHLKDGTC", +"CGLVEVEK", +"YIVGDVVQ", +"YQCAMRPN", +"TVLKKCKS", +"GTCGLVEV", +"DGTCGLVE", +"GSCGSVGF", +"VWTLMNVL", +"LNGSCGSV", +"NGSCGSVG", +"VLKKCKSA", +"SCGSVGFN", +"FLNGSCGS", +"GLVEVEKG", +"SFLNGSCG", +"KTVLKKCK", +"LEQPYVFI", +"FLKKDAPY", +"KVPTDNYI", +"PQLEQPYV", +"VLPQLEQP", +"YENAFLPF", +"WQTGDFVK", +"SWQTGDFV", +"VPTDNYIT", +"LKKDAPYI", +"KGVLPQLE", +"GVLPQLEQ", +"GNKGAGGH", +"GARRVWTL", +"QTGDFVKA", +"AKTVLKKC", +"GSFLNGSC", +"YGADLKSF", +"EKGVLPQL", +"QLEQPYVF", +"CMVQVTCG", +"VEKGVLPQ", +"LPQLEQPY", +"TGDFVKAT", +"ARRVWTLM", +"CGTTTLNG", +"AGALNKAT", +"CGSVGFNI", +"VQVTCGTT", +"LGDELGTD", +"DLGDELGT", +"VEEAKTVL", +"QVTCGTTT", +"DAPYIVGD", +"ETLGVLVP", +"EAKTVLKK", +"WTLMNVLT", +"EDFQENWN", +"TDPYEDFQ", +"MVQVTCGT", +"KGSFLNGS", +"WNTKHSSG", +"VAGALNKA", +"GALNKATN", +"EVEKGVLP", +"GTTTLNGL", +"VEVEKGVL", +"GETLGVLV", +"DFQENWNT", +"VTCGTTTL", +"FQENWNTK", +"GTDLEGNF", +"EEAKTVLK", +"QENWNTKH", +"TLMNVLTL", +"LVEVEKGV", +"NWNTKHSS", +"YEDFQENW", +"KDAPYIVG", +"IKGSFLNG", +"EQPYVFIK", +"PTDNYITT", +"DPYEDFQE", +"TDNYITTY", +"SGETLGVL", +"GDELGTDP", +"YMHHMELP", +"TCGTTTLN", +"TDLEGNFY", +"GVAGALNK", +"CYMHHMEL", +"DLEGNFYG", +"KKDAPYIV", +"HAGTDLEG", +"GTDPYEDF", +"VHAGTDLE", +"PYEDFQEN", +"FCYMHHME", +"AGTDLEGN", +"DNYITTYP", +"NKGAGGHS", +"RTIKVFTT", +"GGVAGALN", +"SVGFNIDY", +"LGTDPYED", +"GSVGFNID", +"ENWNTKHS", +"GYTVEEAK", +"VGFNIDYD", +"YTVEEAKT", +"SFCYMHHM", +"GVHAGTDL", +"NTKHSSGV", +"TIKVFTTV", +"MELPTGVH", +"QPYVFIKR", +"PYVFIKRS", +"ELGTDPYE", +"TVEEAKTV", +"DCVSFCYM", +"DLKSFDLG", +"VLAWLYAA", +"VRTIKVFT", +"TGVHAGTD", +"GECPNFVF", +"RGVYCCRE", +"TIKGSFLN", +"DGYPLECI", +"LPTGVHAG", +"HMELPTGV", +"CVSFCYMH", +"NGECPNFV", +"VSFCYMHH", +"PDGYPLEC", +"FTIKGSFL", +"ELPTGVHA", +"GFNIDYDC", +"VVNAANVY", +"HHMELPTG", +"PTGVHAGT", +"KRGVYCCR", +"CGPDGYPL", +"MHHMELPT", +"GYPLECIK", +"KNADIVEE", +"NFTIKGSF", +"VVQEGVLT", +"LETAQNSV", +"ECPNFVFP", +"VNAANVYL", +"RSGETLGV", +"VVVNAANV", +"IKNADIVE", +"EGNFYGPF", +"DELGTDPY", +"NVLAWLYA", +"YDCVSFCY", +"IQTIVEVQ", +"FNGECPNF", +"FNIDYDCV", +"LLSLREVR", +"LEGNFYGP", +"GPDGYPLE", +"GADLKSFD", +"NAANVYLK", +"ADLKSFDL", +"YITTYPGQ", +"EVRTIKVF", +"NFVFPLNS", +"ITTYPGQG", +"QGLNGYTV", +"GLNGYTVE", +"NYITTYPG", +"VNVLAWLY", +"CPNFVFPL", +"GQGLNGYT", +"AANVYLKH", +"TLSEQLDF", +"SGVTRELM", +"YIKNADIV", +"VYIKNADI", +"KHGGGVAG", +"HCGETSWQ", +"CTLSEQLD", +"VYLKHGGG", +"TTYPGQGL", +"GDVVQEGV", +"REVRTIKV", +"GVTRELMR", +"NIDYDCVS", +"ANVYLKHG", +"VGDVVQEG", +"TSWQTGDF", +"PNFVFPLN", +"GNFYGPFV", +"DTTITVNV", +"LSLREVRT", +"QCAMRPNF", +"GETSWQTG", +"DHCGETSW", +"VAYRKVLL", +"TKHSSGVT", +"LMRELNGG", +"LECIKDLL", +"IQPRVEKK", +"ELMRELNG", +"DYDCVSFC", +"LSEQLDFI", +"IVGDVVQE", +"DVVQEGVL", +"RELMRELN", +"VTRELMRE", +"CGETSWQT", +"AGKASCTL", +"VKPTVVVN", +"KKVKPTVV", +"PVAYRKVL", +"KHSSGVTR", +"IDYDCVSF", +"CDHCGETS", +"SSGVTREL", +"HSSGVTRE", +"CAMRPNFT", +"KVKPTVVV", +"LREVRTIK", +"SCTLSEQL", +"NECNQMCL", +"YVFIKRSD", +"SFSGYLKL", +"SEQLDFID", +"KPTVVVNA", +"FVFPLNSI", +"LNGYTVEE", +"RVEKKKLD", +"PTVVVNAA", +"KVLLRKNG", +"LKHGGGVA", +"QPRVEKKK", +"NGYTVEEA", +"YRKVLLRK", +"TVVVNAAN", +"GGGVAGAL", +"VFIKRSDA", +"NVYLKHGG", +"VVRSIFSR", +"VEKKKLDG", +"FYGPFVDR", +"NVYIKNAD", +"TVNVLAWL", +"KLTDNVYI", +"YPLECIKD", +"TIQPRVEK", +"TRELMREL", +"PLECIKDL", +"PRVEKKKL", +"KCDHCGET", +"GLNDNLLE", +"QTIVEVQP", +"YGPFVDRQ", +"PGQGLNGY", +"LTDNVYIK", +"IKTIQPRV", +"NFKVTKGK", +"RQTAQAAG", +"YPGQGLNG", +"LKLTDNVY", +"RKVLLRKN", +"YLKLTDNV", +"TLLSLREV", +"ITVNVLAW", +"AYRKVLLR", +"TDNVYIKN", +"ECIKDLLA", +"TYPGQGLN", +"TTLNGLWL", +"TLGVLVPH", +"KTIQPRVE", +"LMWLIINL", +"FSGYLKLT", +"EIPVAYRK", +"DRQTAQAA", +"FIKRSDAR", +"IFSRTLET", +"NFYGPFVD", +"YLKHGGGV", +"SIFSRTLE", +"GIQYGRSG", +"VFPLNSII", +"TTTLNGLW", +"LGVLVPHV", +"IKRSDART", +"KRSDARTA", +"DNVYIKNA", +"GEIPVAYR", +"TILTSLLV", +"MKCDHCGE", +"GPFVDRQT", +"GYLKLTDN", +"YTRYVDNN", +"EGIQYGRS", +"VGEIPVAY", +"SLREVRTI", +"GNFKVTKG", +"GRSGETLG", +"LTILTSLL", +"IPVAYRKV", +"HGGGVAGA", +"SGYLKLTD", +"GKASCTLS", +"ECNQMCLS", +"CNQMCLST", +"RSIFSRTL", +"RAGKASCT", +"TITVNVLA", +"IQPGQTFS", +"ENAFLPFA", +"VAELEGIQ", +"LVAELEGI", +"VESCGNFK", +"TLNGLWLD", +"FPLNSIIK", +"LMKCDHCG", +"TLMKCDHC", +"FVDRQTAQ", +"ETSWQTGD", +"TLETAQNS", +"KASCTLSE", +"SCGNFKVT", +"ELVAELEG", +"RVVRSIFS", +"ARAGKASC", +"IIKTIQPR", +"VLVPHVGE", +"KDLLARAG", +"ESCGNFKV", +"QYGRSGET", +"ASCTLSEQ", +"FSRTLETA", +"LKRGDKSV", +"GVLVPHVG", +"CGNFKVTK", +"RSDARTAP", +"RPNFTIKG", +"DLLARAGK", +"AELEGIQY", +"IVESCGNF", +"VDRQTAQA", +"TTITVNVL", +"FKVTKGKA", +"LLARAGKA", +"LARAGKAS", +"CIKDLLAR", +"NQMCLSTL", +"IQYGRSGE", +"PNFTIKGS", +"LLTILTSL", +"RTLETAQN", +"EFLKRGDK", +"ELEGIQYG", +"FLKRGDKS", +"KTLLSLRE", +"EILQKEKV", +"IKDLLARA", +"WLMWLIIN", +"SRTLETAQ", +"PLNSIIKT", +"NGNKGAGG", +"SIIKTIQP", +"FKQIVESC", +"KQIVESCG", +"LEGIQYGR", +"SDARTAPH", +"LNSIIKTI", +"QIVESCGN", +"AMRPNFTI", +"VEEAKKVK", +"ARVVRSIF", +"LLLTILTS", +"NSFSGYLK", +"YGRSGETL", +"MRPNFTIK", +"MRELNGGA", +"KVTKGKAK", +"LNGLWLDD", +"SWLMWLII", +"IVEEAKKV", +"MCLSTLMK", +"NSIIKTIQ", +"QMCLSTLM", +"VKFISTCA", +"CLSTLMKC", +"KRGDKSVY", +"LEILQKEK", +"ELNGGAYT", +"VRSIFSRT", +"INIVGDFK", +"LRKNGNKG", +"VTKGKAKK", +"VGDFKLNE", +"TSAALQPE", +"KNGNKGAG", +"LSTLMKCD", +"LKTLLSLR", +"NAFLPFAM", +"RELNGGAY", +"AWNIGEQK", +"SFDLGDEL", +"KVNINIVG", +"LLEILQKE", +"IVGDFKLN", +"EKVNINIV", +"NIVGDFKL", +"EEAKKVKP", +"NGLWLDDV", +"VLLRKNGN", +"VEVQPQLE", +"STLMKCDH", +"QKEKVNIN", +"PFVDRQTA", +"LLRKNGNK", +"VPHVGEIP", +"WNIGEQKS", +"NINIVGDF", +"ARTAPHGH", +"AFLPFAMG", +"LVPHVGEI", +"DIVEEAKK", +"VNINIVGD", +"FDNLKTLL", +"IVEVQPQL", +"FDLGDELG", +"LDGEVITF", +"NLLEILQK", +"NADIVEEA", +"KEKVNINI", +"DGEVITFD", +"QEGVLTAV", +"VNSFSGYL", +"FHLDGEVI", +"FCGPDGYP", +"GTTEMLAK", +"NFCGPDGY", +"TIVEVQPQ", +"NGGAYTRY", +"AYTRYVDN", +"PHVGEIPV", +"GLWLDDVV", +"GGAYTRYV", +"GEVITFDN", +"NDNLLEIL", +"RKNGNKGA", +"LNGGAYTR", +"LNDNLLEI", +"GRIRSVYP", +"LQKEKVNI", +"HLDGEVIT", +"GAYTRYVD", +"TFHLDGEV", +"IEFLKRGD", +"DNLKTLLS", +"DNLLEILQ", +"NLKTLLSL", +"ADIVEEAK", +"ILQKEKVN", +"GGTTEMLA", +"RGDKSVYY", +"MGRIRSVY", +"LGIEFLKR", +"EVITFDNL", +"NNFCGPDG", +"VQEGVLTA", +"IVKFISTC", +"GIEFLKRG", +"LWLDDVVY", +"LDGFMGRI", +"FMGRIRSV", +"EVQPQLEM", +"EGVLTAVV", +"DGFMGRIR", +"AARVVRSI", +"EAKKVKPT", +"TFDNLKTL", +"ILASFSAS", +"HVGEIPVA", +"VEGCMVQV", +"DARTAPHG", +"VITFDNLK", +"VELVAELE", +"EVNSFSGY", +"IILASFSA", +"NIGEQKSI", +"ITFDNLKT", +"YVDNNFCG", +"IEVNSFSG", +"AIILASFS", +"QLGIEFLK", +"IGEQKSIL", +"TDTTITVN", +"EAARVVRS", +"KFISTCAC", +"ASEAARVV", +"TFLLNKEM", +"VDNNFCGP", +"GFMGRIRS", +"QKSILSPL", +"GDFKLNEE", +"NPTTFHLD", +"TTFHLDGE", +"DNNFCGPD", +"TQLGIEFL", +"TRYVDNNF", +"LEMELTPV", +"EQKSILSP", +"PTTFHLDG", +"RYVDNNFC", +"SILSPLYA", +"AKKVKPTV", +"GEQKSILS", +"KSILSPLY", +"GKVEGCMV", +"GCMVQVTC", +"YKAFKQIV", +"KVEGCMVQ", +"KAFKQIVE", +"AGTDTTIT", +"LSPLYAFA", +"EGCMVQVT", +"AFKQIVES", +"QTAQAAGT", +"QAAGTDTT", +"YSGQSTQL", +"GTDTTITV", +"AAGTDTTI", +"EMELTPVV", +"SGQSTQLG", +"WLLLTILT", +"VQPQLEME", +"STQLGIEF", +"QLEMELTP", +"LASFSAST", +"FKLNEEIA", +"SNPTTFHL", +"IAIILASF", +"LTAVVIPT", +"ISTCACEI", +"QPQLEMEL", +"ILSPLYAF", +"EEIAIILA", +"EIAIILAS", +"QLDFIDTK", +"DTKRGVYC", +"TAVVIPTK", +"FIDTKRGV", +"LDFIDTKR", +"IDTKRGVY", +"DFIDTKRG", +"PQLEMELT", +"GQSTQLGI", +"TKRGVYCC", +"QSTQLGIE", +"DFKLNEEI", +"NEEIAIIL", +"GDKSVYYT", +"AQAAGTDT", +"MELTPVVQ", +"FISTCACE", +"KVENMTPR", +"KSFDLGDE", +"KLNEEIAI", +"AVVIPTKK", +"EQLDFIDT", +"SPLYAFAS", +"LKSFDLGD", +"PLYAFASE", +"LNEEIAII", +"ASFSASTS", +"SYSGQSTQ", +"KDWSYSGQ", +"GVLTAVVI", +"GAWNIGEQ", +"TIEVNSFS", +"KSVYYTSN", +"TKGKAKKG", +"LYAFASEA", +"QTIEVNSF", +"SVYYTSNP", +"KGAWNIGE", +"DKSVYYTS", +"SEAARVVR", +"VLTAVVIP", +"VENMTPRD", +"TAQAAGTD", +"KKGAWNIG", +"RIRSVYPV", +"DWSYSGQS", +"YAFASEAA", +"FASEAARV", +"HWLLLTIL", +"WSYSGQST", +"KAKKGAWN", +"GKAKKGAW", +"AKKGAWNI", +"KGKAKKGA", +"RSVYPVAS", +"VYCPRHVI", +"KEMYLKLR", +"NKEMYLKL", +"VVQTIEVN", +"AFASEAAR", +"VYYTSNPT", +"YCPRHVIC", +"VQTIEVNS", +"TPVVQTIE", +"PVVQTIEV", +"SFSASTSA", +"SASTSAFV", +"ELTPVVQT", +"FSASTSAF", +"ENMTPRDL", +"VVYCPRHV", +"LTPVVQTI", +"YTSNPTTF", +"TSNPTTFH", +"EMYLKLRS", +"NMTPRDLG", +"DVVYCPRH", +"IIAMSAFA", +"RIQPGQTF", +"LLNKEMYL", +"ATSAALQP", +"WLDDVVYC", +"VRIQPGQT", +"YYTSNPTT", +"LNKEMYLK", +"LDDVVYCP", +"QWSLFFFL", +"GIIAMSAF", +"FLLNKEMY", +"MYLKLRSD", +"QQDGSEDN", +"GQQDGSED", +"NGDRWFLN", +"MGIIAMSA", +"DDVVYCPR", +"PQIGVVRE", +"VSDIDITF", +"WSLFFFLY", +"NRPQIGVV", +"INRPQIGV", +"RPQIGVVR", +"SSAINRPQ", +"LKLRSDVL", +"FFFLYENA", +"LFFFLYEN", +"GATSAALQ", +"SLFFFLYE", +"YLKLRSDV", +"GDRWFLNR", +"DVSSAINR", +"VSSAINRP", +"FDKNLYDK", +"DKNLYDKL", +"INGDRWFL", +"LRVIGHSM", +"SFTVLCLT", +"DRWFLNRF", +"LLFLMSFT", +"FVRIQPGQ", +"LPFAMGII", +"LFLMSFTV", +"TLLFLMSF", +"FTVLCLTP", +"SAINRPQI", +"FLMSFTVL", +"TVLCLTPV", +"MSFTVLCL", +"NTLLFLMS", +"HDVSSAIN", +"FNTLLFLM", +"SGKVEGCM", +"EGDCEEEE", +"AINRPQIG", +"AMGIIAMS", +"KFVRIQPG", +"CPRHVICT", +"YKFVRIQP", +"PFAMGIIA", +"FAMGIIAM", +"PRHVICTS", +"EEGDCEEE", +"FKLASHMY", +"DCEEEEFE", +"NHNFLVQA", +"HVICTSED", +"LMSFTVLC", +"LASHMYCS", +"NFLVQAGN", +"KNLYDKLV", +"LVQAGNVQ", +"VICTSEDM", +"FLVQAGNV", +"KLASHMYC", +"NPNYEDLL", +"PNYEDLLI", +"EEEGDCEE", +"HNFLVQAG", +"RHVICTSE", +"DMLNPNYE", +"YYLFDESG", +"MLNPNYED", +"DEEEGDCE", +"SNHNFLVQ", +"PSGKVEGC", +"GDCEEEEF", +"TYYLFDES", +"EDEEEGDC", +"CEEEEFEP", +"HMYCSFYP", +"MTPRDLGA", +"LNPNYEDL", +"SHMYCSFY", +"FCGTENLT", +"VQAGNVQL", +"NYEDLLIR", +"ASHMYCSF", +"QLRVIGHS", +"YEDLLIRK", +"FLYENAFL", +"EDMLNPNY", +"LLIRKSNH", +"EDLLIRKS", +"DLLIRKSN", +"ILLLDQAL", +"LLLDQALV", +"KSNHNFLV", +"LDEWSMAT", +"PNECNQMC", +"AGNVQLRV", +"LIRKSNHN", +"CGTENLTK", +"SEDMLNPN", +"STCACEIV", +"NVQLRVIG", +"QAGNVQLR", +"MYCSFYPP", +"IRKSNHNF", +"WSMATYYL", +"SMATYYLF", +"RKSNHNFL", +"VQLRVIGH", +"LYENAFLP", +"ICTSEDML", +"GNVQLRVI", +"DEWSMATY", +"EWSMATYY", +"SPNECNQM", +"NSWLMWLI", +"MATYYLFD", +"SNSWLMWL", +"FPSGKVEG", +"ATYYLFDE", +"NKYKYFSG", +"QDGSEDNQ", +"CTSEDMLN", +"HFISNSWL", +"FISNSWLM", +"TSEDMLNP", +"DEDEEEGD", +"ISNSWLMW", +"AFPSGKVE", +"ACSHAAVD", +"TQYEYGTE", +"YNKYKYFS", +"MAFPSGKV", +"KHFSMMIL", +"GTENLTKE", +"LQSGFRKM", +"VINGDRWF", +"KMAFPSGK", +"YVFCTVNA", +"FRKMAFPS", +"SITSAVLQ", +"QYVFCTVN", +"RKMAFPSG", +"LYNKYKYF", +"EQYVFCTV", +"TACSHAAV", +"AVINGDRW", +"ASPNECNQ", +"LEQYVFCT", +"QSGFRKMA", +"VYKVYYGN", +"ATNYDLSV", +"CTFLLNKE", +"FFLYENAF", +"AAVINGDR", +"MATNYDLS", +"EGLNDNLL", +"VLQSGFRK", +"TENLTKEG", +"HAAVDALC", +"AVLQSGFR", +"SMATNYDL", +"GSEDNQTT", +"PDEDEEEG", +"SHAAVDAL", +"GFRKMAFP", +"ITSAVLQS", +"YCSFYPPD", +"YAAVINGD", +"LYAAVING", +"CSHAAVDA", +"TSAVLQSG", +"SAVLQSGF", +"NDVSFLAH", +"LCTFLLNK", +"DGSEDNQT", +"VVFDEISM", +"IYLYLTFY", +"YREAACCH", +"PIDKCSRI", +"VLCLTPVY", +"SGFRKMAF", +"SDVLYQPP", +"CFDKFKVN", +"FLPFAMGI", +"LPIDKCSR", +"GEFKLASH", +"ISMATNYD", +"CSFYPPDE", +"YTACSHAA", +"YMLTYNKV", +"KALKYLPI", +"DVSFLAHI", +"DEISMATN", +"VFDEISMA", +"TNDVSFLA", +"EISMATNY", +"SYREAACC", +"FYPPDEDE", +"ALCTFLLN", +"FDEISMAT", +"KYLPIDKC", +"ALKYLPID", +"EFKLASHM", +"VYTACSHA", +"YPPDEDEE", +"TLEQYVFC", +"LKYLPIDK", +"GHSMQNCV", +"YLPIDKCS", +"SFYPPDED", +"LFDESGEF", +"FDESGEFK", +"NYMLTYNK", +"NNYMLTYN", +"SVIYLYLT", +"FLPGVYSV", +"FGATSAAL", +"KYKYFSGA", +"VIYLYLTF", +"VDALCEKA", +"YLRKHFSM", +"PPDEDEEE", +"YSVIYLYL", +"IVVFDEIS", +"VYSVIYLY", +"LVYKVYYG", +"LRKHFSMM", +"RKHFSMMI", +"VIGHSMQN", +"EPSTQYEY", +"YLFDESGE", +"IGHSMQNC", +"FKMFYKGV", +"GSDVLYQP", +"DVLYQPPQ", +"AALCTFLL", +"EEEEFEPS", +"TPVYSFLP", +"SGEFKLAS", +"EFGATSAA", +"DESGEFKL", +"RVIGHSMQ", +"AKHYVYIG", +"AVDALCEK", +"KHYVYIGD", +"YEYGTEDD", +"FDKFKVNS", +"LCLTPVYS", +"HSMQNCVL", +"IVYTACSH", +"EEEFEPST", +"LPGVYSVI", +"AAVDALCE", +"EYGTEDDY", +"ESGEFKLA", +"NVLTLVYK", +"VLTLVYKV", +"TEDDYQGK", +"PSTQYEYG", +"YGTEDDYQ", +"MNVLTLVY", +"LTPVYSFL", +"EFEPSTQY", +"CLTPVYSF", +"RIVYTACS", +"THDVSSAI", +"KMFYKGVI", +"QYEYGTED", +"HYVYIGDP", +"ARIVYTAC", +"TLVYKVYY", +"PGVYSVIY", +"IRSVYPVA", +"AYLRKHFS", +"GTEDDYQG", +"GKSHFAIG", +"YAYLRKHF", +"NSTLEQYV", +"KSHFAIGL", +"STLEQYVF", +"GVYSVIYL", +"EEFEPSTQ", +"STQYEYGT", +"KYKFVRIQ", +"SARIVYTA", +"ENLTKEGA", +"CEKALKYL", +"TSYREAAC", +"FYAYLRKH", +"NRDVDTDF", +"SEGLNDNL", +"TNYDLSVV", +"LTLVYKVY", +"RNRDVDTD", +"FEPSTQYE", +"FCTVNALP", +"YVYIGDPA", +"PKYKFVRI", +"YYPSARIV", +"RDVDTDFV", +"VFCTVNAL", +"TPKYKFVR", +"EKALKYLP", +"KFKVNSTL", +"ITHDVSSA", +"TTSYREAA", +"PVYSFLPG", +"YLYLTFYL", +"TGKSHFAI", +"GTGKSHFA", +"DALCEKAL", +"ALCEKALK", +"FKVNSTLE", +"LCEKALKY", +"DVDTDFVN", +"NYDLSVVN", +"VNSTLEQY", +"CTVNALPE", +"DKFKVNST", +"REAACCHL", +"KVNSTLEQ", +"DLSVVNAR", +"KALNDFSN", +"TCACEIVG", +"YDLSVVNA", +"SMQNCVLK", +"LEFGATSA", +"VYPVASPN", +"YPSARIVY", +"TVNALPET", +"QALVSDVG", +"DTDFVNEF", +"YLTFYLTN", +"LYLTFYLT", +"VYSFLPGV", +"VSFLAHIQ", +"FVNEFYAY", +"EFYAYLRK", +"YPVASPNE", +"VASPNECN", +"VDTDFVNE", +"VNEFYAYL", +"LTFYLTND", +"DFVNEFYA", +"NEFYAYLR", +"YLTNDVSF", +"FSNSGSDV", +"PSARIVYT", +"FYLTNDVS", +"LTNDVSFL", +"DIVVFDEI", +"DQALVSDV", +"YSFLPGVY", +"TFYLTNDV", +"LMNVLTLV", +"SFLPGVYS", +"KTPKYKFV", +"PVASPNEC", +"DTTSYREA", +"WLYAAVIN", +"NDFSNSGS", +"SVYPVASP", +"LNDFSNSG", +"LRAKHYVY", +"DFSNSGSD", +"SFLAHIQW", +"SNSGSDVL", +"VGQQDGSE", +"LYYPSARI", +"LYYQNNVF", +"TDFVNEFY", +"DVLVRGFG", +"LSVVNARL", +"LDQALVSD", +"HFSMMILS", +"SGSDVLYQ", +"NSGSDVLY", +"AKALNDFS", +"LLDQALVS", +"EAACCHLA", +"AACCHLAK", +"YYQNNVFM", +"VNALPETT", +"YQNNVFMS", +"ALVSDVGD", +"ADIVVFDE", +"FSMMILSD", +"CCHLAKAL", +"ALNDFSNS", +"VLVRGFGD", +"ACCHLAKA", +"FLAHIQWM", +"SMMILSDD", +"CHLAKALN", +"RLRAKHYV", +"GPPGTGKS", +"ARLRAKHY", +"LAKALNDF", +"LAHIQWMV", +"HLAKALND", +"QGPPGTGK", +"NARLRAKH", +"VNARLRAK", +"VVNARLRA", +"VMLTNDNT", +"YKGVITHD", +"FYKGVITH", +"YSVMLTND", +"SVMLTNDN", +"VITHDVSS", +"KGVITHDV", +"GVITHDVS", +"AHIQWMVM", +"VLYYQNNV", +"SVVNARLR", +"FTGYRVTK", +"NLTKEGAT", +"QNNVFMSE", +"GSEGLNDN", +"NALPETTA", +"TADIVVFD", +"MLTNDNTS", +"ALPETTAD", +"GPLSAQTG", +"NNVFMSEA", +"TNDNTSRY", +"LTNDNTSR", +"VFTGYRVT", +"PPGTGKSH", +"EFTPFDVV", +"EDEFTPFD", +"YVFTGYRV", +"NYVFTGYR", +"TTADIVVF", +"PETTADIV", +"CASLKELL", +"FMSEAKCW", +"LPETTADI", +"DEFTPFDV", +"ETTADIVV", +"VFMSEAKC", +"SVLYYQNN", +"MCASLKEL", +"KSVLYYQN", +"EAALCTFL", +"NVFMSEAK", +"FKSVLYYQ", +"FCAGSTFI", +"MFYKGVIT", +"VVLKTGDL", +"FDVVRQCS", +"PGTGKSHF", +"DAVVCFNS", +"PLSAQTGI", +"LEDEFTPF", +"GDPAQLPA", +"ASLKELLQ", +"MMILSDDA", +"MILSDDAV", +"DVVRQCSG", +"VLKTGDLQ", +"LGPLSAQT", +"GSTFISDE", +"EYFNSVCR", +"AVVCFNST", +"FTPFDVVR", +"ILSDDAVV", +"AGSTFISD", +"NFKSVLYY", +"AWLYAAVI", +"VVCFNSTY", +"PFDVVRQC", +"MSEAKCWT", +"LKTGDLQP", +"LQGPPGTG", +"PAQLPAPR", +"DPAQLPAP", +"STFISDEV", +"TPFDVVRQ", +"QCSGVTFQ", +"VRQCSGVT", +"LSDDAVVC", +"LEPEYFNS", +"AQLPAPRT", +"VYIGDPAQ", +"LVSDVGDS", +"EPEYFNSV", +"DDAVVCFN", +"VCFNSTYA", +"SLKELLQN", +"CAGSTFIS", +"DMCASLKE", +"KNFKSVLY", +"LKELLQNG", +"RQCSGVTF", +"LLEDEFTP", +"VVRQCSGV", +"SDDAVVCF", +"CFNSTYAS", +"QLPAPRTL", +"SIKNFKSV", +"IKNFKSVL", +"FNSTYASQ", +"PEYFNSVC", +"CSGVTFQS", +"NSTYASQG", +"YIGDPAQL", +"VDILGPLS", +"RAKHYVYI", +"STYASQGL", +"FEEAALCT", +"FAIGLALY", +"EEAALCTF", +"ALLEDEFT", +"TFEEAALC", +"ILGPLSAQ", +"AKCWTETD", +"DILGPLSA", +"LLYIDING", +"ASIKNFKS", +"LYIDINGN", +"QDHVDILG", +"LSAQTGIA", +"KCWTETDL", +"KELLQNGM", +"ETAQNSVR", +"CFKMFYKG", +"LPAPRTLL", +"TLQGPPGT", +"TQDHVDIL", +"KTGDLQPL", +"LTQDHVDI", +"DHVDILGP", +"QCFKMFYK", +"SEAKCWTE", +"HVDILGPL", +"LLLYIDIN", +"YFNSVCRL", +"MKTIGPDM", +"CACEIVGG", +"NYEPLTQD", +"IGDPAQLP", +"FNSVCRLM", +"KTIGPDMF", +"AQCFKMFY", +"PAPRTLLT", +"LDMCASLK", +"NSVCRLMK", +"STFEEAAL", +"HFAIGLAL", +"YNYEPLTQ", +"TIGPDMFL", +"SVCRLMKT", +"SMKYFVKI", +"PYPDPSRI", +"VSDVGDSA", +"MKYFVKIG", +"YPDPSRIL", +"SALLEDEF", +"CWTETDLT", +"AIGLALYY", +"LPYPDPSR", +"VCRLMKTI", +"EAKCWTET", +"LTENLLLY", +"FSTFEEAA", +"APRTLLTK", +"AQNSVRVL", +"FLTENLLL", +"ACEIVGGQ", +"TAQNSVRV", +"HMLDMYSV", +"NLLLYIDI", +"KFLTENLL", +"TENLLLYI", +"ENLLLYID", +"YEPLTQDH", +"MLDMYSVM", +"GHMLDMYS", +"CNNYMLTY", +"YLATALLT", +"SCNNYMLT", +"CRLMKTIG", +"LDMYSVML", +"HHWLLLTI", +"YLPYPDPS", +"LQQIELKF", +"VDTANPKT", +"PRTLLTKG", +"MYSVMLTN", +"LMKTIGPD", +"EPLTQDHV", +"DMYSVMLT", +"TKNSKVQI", +"GSALLEDE", +"DTANPKTP", +"NPKTPKYK", +"ANPKTPKY", +"TGHMLDMY", +"SAQCFKMF", +"RLMKTIGP", +"PKTPKYKF", +"KNSKVQIG", +"VLDMCASL", +"TGDLQPLE", +"SAQTGIAV", +"QNSVRVLQ", +"CEIVGGQI", +"PLTQDHVD", +"WTETDLTK", +"TFCAGSTF", +"TANPKTPK", +"IGLALYYP", +"STLQGPPG", +"TLEPEYFN", +"AQTGIAVL", +"LAIDAYPL", +"ALNKATNN", +"VKRTIKGT", +"GPHEFCSQ", +"GLALYYPS", +"EGSEGLND", +"QTGIAVLD", +"SHFAIGLA", +"LTGHMLDM", +"THHWLLLT", +"SLAIDAYP", +"SDVGDSAE", +"ELLQNGMN", +"DKSAQCFK", +"LALYYPSA", +"KDKSAQCF", +"ELTGHMLD", +"VYLPYPDP", +"PDPSRILG", +"KGPHEFCS", +"LGSALLED", +"TGIAVLDM", +"KYFVKIGP", +"KSAQCFKM", +"LNKATNNA", +"VTKNSKVQ", +"LQYIRKLH", +"LGAGCFVD", +"GTLEPEYF", +"FHLYLQYI", +"MWLIINLV", +"LYLQYIRK", +"DYVYLPYP", +"TKFLTENL", +"TGYRVTKN", +"GIAVLDMC", +"VSLAIDAY", +"LEETKFLT", +"ILGAGCFV", +"YLQYIRKL", +"HLYLQYIR", +"GVTFQSAV", +"VFHLYLQY", +"RDVLVRGF", +"KGTLEPEY", +"RKLHDELT", +"RILGAGCF", +"YADVFHLY", +"WLIINLVQ", +"ILGSALLE", +"HKDKSAQC", +"YVYLPYPD", +"KCSRIIPA", +"GYRVTKNS", +"GAGCFVDD", +"DPSRILGA", +"DDYVYLPY", +"YIRKLHDE", +"SGVTFQSA", +"LHDELTGH", +"IRKLHDEL", +"QYIRKLHD", +"CSRIIPAR", +"DELTGHML", +"TFQSAVKR", +"KLHDELTG", +"ALYYPSAR", +"SRILGAGC", +"GDSAEVAV", +"ETKFLTEN", +"VGDSAEVA", +"FVDDIVKT", +"HDELTGHM", +"LLQNGMNG", +"IAVLDMCA", +"DKCSRIIP", +"IDKCSRII", +"EETKFLTE", +"DSCNNYML", +"VDDIVKTD", +"DVFHLYLQ", +"DVGDSAEV", +"DTFCAGST", +"VKTDGTLM", +"YRVTKNSK", +"PSRILGAG", +"LIINLVQM", +"AVLDMCAS", +"CDTFCAGS", +"AHKDKSAQ", +"LATALLTL", +"ADVFHLYL", +"VRDVLVRG", +"KTDGTLMI", +"GGKGFCKL", +"TILGSALL", +"GMNGRTIL", +"VECFDKFK", +"SRIIPARA", +"GEGSEGLN", +"AGCFVDDI", +"KIKACVEE", +"RVTKNSKV", +"PHEFCSQH", +"EVTTTLEE", +"KAHKDKSA", +"IIPARARV", +"LTLQQIEL", +"RIIPARAR", +"LKAHKDKS", +"HEFCSQHT", +"EVAVKMFD", +"RTLLTKGT", +"TLLTKGTL", +"TLEETKFL", +"TLQQIELK", +"PARARVEC", +"CFVDDIVK", +"RFVSLAID", +"TKGTLEPE", +"FVSLAIDA", +"ERFVSLAI", +"DDIVKTDG", +"FQSAVKRT", +"RVECFDKF", +"IKACVEEV", +"LLTKGTLE", +"DIVKTDGT", +"IVKTDGTL", +"IINLVQMA", +"GCFVDDIV", +"GKGFCKLH", +"ECFDKFKV", +"EEVTTTLE", +"NTFSSTFN", +"ARARVECF", +"TDGTLMIE", +"MNGRTILG", +"KLRSDVLL", +"TFSSTFNV", +"KLNVGDYF", +"VNTFSSTF", +"YKLNVGDY", +"IERFVSLA", +"DGTLMIER", +"NSKVQIGE", +"TLMIERFV", +"RWFLNRFT", +"TTLEETKF", +"GTLMIERF", +"LLTLQQIE", +"LQNGMNGR", +"EYADVFHL", +"VEEVTTTL", +"VTFQSAVK", +"LMIERFVS", +"RARVECFD", +"ARVECFDK", +"CSQHTMLV", +"IDAYPLTK", +"YVNTFSST", +"NVLSTFIS", +"LRSDVLLP", +"MIERFVSL", +"LDNVLSTF", +"NGMNGRTI", +"FSSTFNVP", +"LNVGDYFV", +"DNVLSTFI", +"AIDAYPLT", +"ACVEEVTT", +"KGFCKLHN", +"LTKGPHEF", +"QNGMNGRT", +"YSTLQGPP", +"KACVEEVT", +"TYKLNVGD", +"TKGPHEFC", +"AEVAVKMF", +"CVEEVTTT", +"SAEVAVKM", +"AVKRTIKG", +"TTTLEETK", +"SQHTMLVK", +"QSAVKRTI", +"NGRTILGS", +"VYANGGKG", +"DSAEVAVK", +"GFCKLHNW", +"CQPILLLD", +"DKKIKACV", +"DAYPLTKH", +"VTTTLEET", +"MFDAYVNT", +"RTILGSAL", +"KTLVATAE", +"NQEYADVF", +"VLSTFISA", +"AYVNTFSS", +"DAYVNTFS", +"KPFITESK", +"GRTILGSA", +"QEYADVFH", +"NGGKGFCK", +"FCKLHNWN", +"FDAYVNTF", +"LTKGTLEP", +"EFCSQHTM", +"KLHNWNCV", +"CKLHNWNC", +"EVKPFITE", +"VKPFITES", +"VECLKLSH", +"FYVYANGG", +"KHPNQEYA", +"YVYANGGK", +"AYPLTKHP", +"VAVKMFDA", +"ANGGKGFC", +"KKIKACVE", +"YANGGKGF", +"TLVATAEA", +"SAVKRTIK", +"PNQEYADV", +"ECLKLSHQ", +"SFYVYANG", +"FITESKPS", +"HPNQEYAD", +"PILLLDQA", +"KVQIGEYT", +"VKMFDAYV", +"FLNRFTTT", +"LKTLVATA", +"LHNWNCVN", +"NVSLDNVL", +"VRRSFYVY", +"WFLNRFTT", +"GVRRSFYV", +"SKVQIGEY", +"RRSFYVYA", +"AVKMFDAY", +"VSLDNVLS", +"VNCDTFCA", +"YPLTKHPN", +"CLKLSHQS", +"TKHPNQEY", +"KMFDAYVN", +"DLTKGPHE", +"WNCVNCDT", +"NCDTFCAG", +"EAELAKNV", +"ITESKPSV", +"GTHHWLLL", +"NKATNNAM", +"TTYKLNVG", +"LTKHPNQE", +"FCSQHTML", +"NWNCVNCD", +"NSVRVLQK", +"NCVNCDTF", +"PLTKHPNQ", +"LVATAEAE", +"GDYGDAVV", +"RVECTTIV", +"RSFYVYAN", +"GDDYVYLP", +"ATAEAELA", +"LKLSHQSD", +"AEAELAKN", +"VNGVRRSF", +"QPILLLDQ", +"KGDYGDAV", +"LNRFTTTL", +"SLDNVLST", +"NGVRRSFY", +"TIVNGVRR", +"KNVSLDNV", +"HNWNCVNC", +"WKSYVHVV", +"TAEAELAK", +"VQIGEYTF", +"CVNCDTFC", +"IVNGVRRS", +"CTTIVNGV", +"ASFYYVWK", +"VATAEAEL", +"QHTMLVKQ", +"FYYVWKSY", +"TETDLTKG", +"FEKGDYGD", +"EKLKTLVA", +"EKGDYGDA", +"VWKSYVHV", +"KLSHQSDI", +"TPRDLGAC", +"AKNVSLDN", +"KLKTLVAT", +"VASIKNFK", +"YYVWKSYV", +"IRHVRAWI", +"LVASIKNF", +"ETDLTKGP", +"YFVKIGPE", +"TDLTKGPH", +"PSVEQRKQ", +"LAKNVSLD", +"TYASQGLV", +"YVWKSYVH", +"SFYYVWKS", +"SKPSVEQR", +"PFITESKP", +"EYTFEKGD", +"TESKPSVE", +"KPSVEQRK", +"GEYTFEKG", +"YASQGLVA", +"SQGLVASI", +"FVKIGPER", +"EEVKPFIT", +"ECTTIVNG", +"QGLVASIK", +"VKIGPERT", +"IGEYTFEK", +"ELAKNVSL", +"KEEVKPFI", +"GLVASIKN", +"QRKQDDKK", +"ASQGLVAS", +"VECTTIVN", +"KYSTLQGP", +"KQDDKKIK", +"RKQDDKKI", +"TTIVNGVR", +"YTFEKGDY", +"LSTFISAA", +"EIVKFIST", +"TTTYKLNV", +"TFEKGDYG", +"FFASFYYV", +"ESKPSVEQ", +"QIGEYTFE", +"FASFYYVW", +"KIGPERTC", +"AELAKNVS", +"TFISDEVA", +"KGTHHWLL", +"LSHQSDIE", +"IFFASFYY", +"HQSDIEVT", +"IGPERTCC", +"YFVLTSHT", +"SHQSDIEV", +"QGDDYVYL", +"DYFVLTSH", +"FVLTSHTV", +"KQGDDYVY", +"INLVQMAP", +"NLVQMAPI", +"VKQGDDYV", +"YVHVVDGC", +"QDDKKIKA", +"MLVKQGDD", +"LVKQGDDY", +"EVVLKTGD", +"DDKKIKAC", +"YIFFASFY", +"EQRKQDDK", +"GDYFVLTS", +"NSSTCMMC", +"VGDYFVLT", +"FISDEVAR", +"VHVVDGCN", +"HVVDGCNS", +"VQMAPISA", +"SVEQRKQD", +"VEQRKQDD", +"KSYVHVVD", +"NVGDYFVL", +"SYVHVVDG", +"KDVVECLK", +"ISDEVARD", +"YKYFSGAM", +"KATNNAMQ", +"GTTTYKLN", +"MYIFFASF", +"EEVVLKTG", +"VVDGCNSS", +"CNSSTCMM", +"QKYSTLQG", +"LVQMAPIS", +"WEIVKFIS", +"DVVECLKL", +"IGPDMFLG", +"GLYPTLNI", +"RRCPAEIV", +"CRRCPAEI", +"RGTTTYKL", +"TMLVKQGD", +"GCNSSTCM", +"VDGCNSST", +"FSSNVANY", +"EDDYQGKP", +"YRGTTTYK", +"QMAPISAM", +"DGCNSSTC", +"PKEEVKPF", +"VRMYIFFA", +"SSNVANYQ", +"PRDLGACI", +"DYQGKPLE", +"LTSHTVMP", +"RMYIFFAS", +"ATNNAMQV", +"AMVRMYIF", +"VVECLKLS", +"VRITGLYP", +"KEIIFLEG", +"RITGLYPT", +"TSHTVMPL", +"NVANYQKV", +"YRKCVKSR", +"TKDVVECL", +"ITGLYPTL", +"IIFLEGET", +"MVRMYIFF", +"RKCVKSRE", +"VVYRGTTT", +"SNVANYQK", +"PQEHYVRI", +"LVPQEHYV", +"NLYDKLVS", +"HYVRITGL", +"VPQEHYVR", +"TGLYPTLN", +"GPDMFLGT", +"QEHYVRIT", +"HTMLVKQG", +"VYRGTTTY", +"ETKDVVEC", +"TCRRCPAE", +"PLEFGATS", +"MAPISAMV", +"YVRITGLY", +"PDMFLGTC", +"EHYVRITG", +"DYGDAVVY", +"AVVYRGTT", +"VANYQKVG", +"PKEIIFLE", +"VLTSHTVM", +"DAVVYRGT", +"KCVKSREE", +"EIPKEEVK", +"DDYQGKPL", +"YQGKPLEF", +"DVETKDVV", +"EIIFLEGE", +"VETKDVVE", +"SDVETKDV", +"IVDTVSAL", +"KPLEFGAT", +"ALLTLQQI", +"KSEKQVEQ", +"IPKEEVKP", +"EIVDTVSA", +"IKGTHHWL", +"YFSGAMDT", +"LYRKCVKS", +"TIKGTHHW", +"DTVSALVY", +"SFLEMKSE", +"PAEIVDTV", +"TVSALVYD", +"TEEVVLKT", +"LYDKLVSS", +"QGKPLEFG", +"MKSEKQVE", +"LYPTLNIS", +"LTEEVVLK", +"YGDAVVYR", +"VDTVSALV", +"GKPLEFGA", +"DSDVETKD", +"GDAVVYRG", +"EFSSNVAN", +"ANYQKVGM", +"MQKYSTLQ", +"SDEVARDL", +"GDSCNNYM", +"RTIKGTHH", +"AEIVDTVS", +"VSALVYDN", +"VDSDVETK", +"WDLLKYDF", +"GFVDSDVE", +"EMKSEKQV", +"KYFSGAMD", +"SAMVRMYI", +"KWDLLKYD", +"TGDSCNNY", +"GMQKYSTL", +"QGFVDSDV", +"PISAMVRM", +"SHTVMPLS", +"VGMQKYST", +"YQKVGMQK", +"FSGAMDTT", +"MDTTSYRE", +"RDLGACID", +"ATALLTLQ", +"QKVGMQKY", +"VTGDSCNN", +"HTVMPLSA", +"KVGMQKYS", +"ISAMVRMY", +"NYQKVGMQ", +"APKEIIFL", +"VGEGSEGL", +"PLKAPKEI", +"FLEMKSEK", +"LEMKSEKQ", +"MPLKAPKE", +"GLYRKCVK", +"APISAMVR", +"CPAEIVDT", +"DLLKYDFT", +"LLKYDFTE", +"IKWDLLKY", +"DLGACIDC", +"KAPKEIIF", +"RQGFVDSD", +"YIKWDLLK", +"FVDSDVET", +"LKAPKEII", +"TALLTLQQ", +"SVRVLQKA", +"YDKLVSSF", +"DKLVSSFL", +"GWEIVKFI", +"CVKSREET", +"TLVPQEHY", +"KSREETGL", +"KRTIKGTH", +"TVMPLSAP", +"SGAMDTTS", +"AEIPKEEV", +"VKSREETG", +"RCPAEIVD", +"GAMDTTSY", +"AMDTTSYR", +"NRNYVFTG", +"YNTYKNTC", +"LGACIDCS", +"IFLEGETL", +"IAEIPKEE", +"PTLVPQEH", +"FLEGETLP", +"VNKFLALC", +"APTLVPQE", +"QVRDVLVR", +"DMFLGTCR", +"VLTEEVVL", +"MFLGTCRR", +"SAPTLVPQ", +"KGLYRKCV", +"LSAPTLVP", +"ARQGFVDS", +"PLSAPTLV", +"DIEVTGDS", +"TFISAARQ", +"FISAARQG", +"LEGETLPT", +"GPERTCCL", +"RNYVFTGY", +"LNRNYVFT", +"STFISAAR", +"SAARQGFV", +"AARQGFVD", +"ISAARQGF", +"EVTGDSCN", +"DYNTYKNT", +"SKGLYRKC", +"DEVARDLS", +"KLVSSFLE", +"SDIEVTGD", +"LMPLKAPK", +"EGETLPTE", +"EETGLLMP", +"QSDIEVTG", +"LVNKFLAL", +"IDCSARHI", +"PDYNTYKN", +"PLNRNYVF", +"FFKLVNKF", +"VIPDYNTY", +"IPDYNTYK", +"VVIPDYNT", +"GACIDCSA", +"REETGLLM", +"LVSSFLEM", +"TVSVSSPD", +"KLVNKFLA", +"IEVTGDSC", +"CIDCSARH", +"VSSFLEMK", +"VMPLSAPT", +"SREETGLL", +"FGTVYEKL", +"IPARARVE", +"PAMHAASG", +"FKLVNKFL", +"MPLSAPTL", +"ETGLLMPL", +"LLMPLKAP", +"NKFLALCA", +"TFFKLVNK", +"HSKGLYRK", +"GLLMPLKA", +"QTFFKLVN", +"MVVIPDYN", +"ACIDCSAR", +"DGWEIVKF", +"VQTFFKLV", +"LKYDFTEE", +"NTYKNTCD", +"KESVQTFF", +"IDAMMFTS", +"TGLLMPLK", +"KYDFTEER", +"EIVGGQIV", +"SEKQVEQK", +"LNLGETFV", +"KVDTANPK", +"DFTEERLK", +"RDGWEIVK", +"ALNLGETF", +"TYKNTCDG", +"MMFTSDLA", +"YDFTEERL", +"AMMFTSDL", +"MCQPILLL", +"KQVEQKIA", +"QVEQKIAE", +"FTEERLKL", +"LMCQPILL", +"DAMMFTSD", +"KFLALCAD", +"EERLKLFD", +"EKQVEQKI", +"TEERLKLF", +"LTKEGATT", +"YDFVENPD", +"LQVRDVLV", +"IVGGQIVT", +"VEQKIAEI", +"PERTCCLC", +"ESVQTFFK", +"GETLPTEV", +"LKALNLGE", +"PYIKWDLL", +"EVLTEEVV", +"VVAFNTLL", +"KPYIKWDL", +"LTKPYIKW", +"FLGTCRRC", +"FLALCADS", +"TKPYIKWD", +"LGTCRRCP", +"DFVENPDI", +"SLRLIDAM", +"DLTKPYIK", +"VAFNTLLF", +"LRLIDAMM", +"LIDAMMFT", +"RLIDAMMF", +"ETLPTEVL", +"GTCRRCPA", +"TLDNQDLN", +"NLGETFVT", +"LDNQDLNG", +"SVQTFFKL", +"FVENPDIL", +"KLKALNLG", +"KALNLGET", +"AFNTLLFL", +"VFDKNLYD", +"MFTSDLAT", +"MHAASGNL", +"LRDGWEIV", +"EQKIAEIP", +"YSLRLIDA", +"ISQYSLRL", +"ERTCCLCD", +"IKESVQTF", +"SDLATNNL", +"DGISQYSL", +"FTSDLATN", +"LGETFVTH", +"ITGGVVQL", +"GETFVTHS", +"DLATNNLV", +"SSFLEMKS", +"GISQYSLR", +"QKIAEIPK", +"VLTLDNQD", +"LTLDNQDL", +"KGIKIQEG", +"GIKIQEGV", +"TSDLATNN", +"KIAEIPKE", +"IKIQEGVV", +"AVFDKNLY", +"SQYSLRLI", +"TLPTEVLT", +"TDLTKPYI", +"FVTHSKGL", +"AMHAASGN", +"HVVAFNTL", +"RTCCLCDR", +"GVLTLDNQ", +"VENPDILR", +"TGGVVQLT", +"ENPDILRV", +"LVYAADPA", +"YITGGVVQ", +"ARFYFYTS", +"VTHSKGLY", +"VGVLTLDN", +"KIQEGVVD", +"DILRVYAN", +"AKLKALNL", +"LATNNLVV", +"ALYNKYKY", +"YGARFYFY", +"EIKESVQT", +"THSKGLYR", +"TFVTHSKG", +"TNNAMQVE", +"SYYSLLMP", +"QYSLRLID", +"LAVFDKNL", +"DSYYSLLM", +"GARFYFYT", +"LALYNKYK", +"ETFVTHSK", +"ATNNLVVM", +"ATVSVSSP", +"VRVLQKAA", +"GGAKLKAL", +"GAKLKALN", +"TCCLCDRR", +"VDSYYSLL", +"TEVLTEEV", +"RYLALYNK", +"YLALYNKY", +"DNQDLNGN", +"PATVSVSS", +"IQEGVVDY", +"AKEIKESV", +"VVDSYYSL", +"ERLKLFDR", +"KEIKESVQ", +"LDGISQYS", +"GTVYEKLK", +"QLMCQPIL", +"PVVDSYYS", +"CCLCDRRA", +"LLVYAADP", +"KVPATVSV", +"IIGGAKLK", +"VLQVRDVL", +"YKGIKIQE", +"VFTTVDNI", +"IGGAKLKA", +"DPAMHAAS", +"KYKGIKIQ", +"WYDFVENP", +"DYGARFYF", +"VPVVDSYY", +"NRYLALYN", +"ASGNLLLD", +"HAASGNLL", +"GVPVVDSY", +"DTDLTKPY", +"IVGVLTLD", +"AASGNLLL", +"GIVGVLTL", +"ELLVYAAD", +"NQDLNGNW", +"FTTVDNIN", +"IDLDEWSM", +"AGIVGVLT", +"GGVVQLTS", +"ILDGISQY", +"NIFGTVYE", +"NAGIVGVL", +"IFGTVYEK", +"QDLNGNWY", +"VPATVSVS", +"YLAVFDKN", +"NDNTSRYW", +"SGVPVVDS", +"VVQLTSQW", +"LTNIFGTV", +"LIDSYFVV", +"QWLTNIFG", +"IDSYFVVK", +"GNLLLDKR", +"QEGVVDYG", +"WNLREMLA", +"RLKLFDRY", +"VQLTSQWL", +"WYDFGDFI", +"SGNLLLDK", +"GVVQLTSQ", +"NLLLDKRT", +"VDYGARFY", +"GVVDYGAR", +"LKLFDRYF", +"KLFDRYFK", +"WLTNIFGT", +"KIFVDGVP", +"LFDRYFKY", +"LALCADSI", +"DLNGNWYD", +"NWYDFGDF", +"NGNWYDFG", +"TVYEKLKP", +"VVDYGARF", +"LNGNWYDF", +"GNWYDFGD", +"VDTDLTKP", +"TNIFGTVY", +"PTEVLTEE", +"GIDLDEWS", +"DWYDFVEN", +"PPLNRNYV", +"GSGVPVVD", +"KELLVYAA", +"FGDFIQTT", +"EGVVDYGA", +"NNLVVMAY", +"YYSLLMPI", +"RNAGIVGV", +"LGIDLDEW", +"YDFGDFIQ", +"HTFSNYQH", +"RKIFVDGV", +"AYITGGVV", +"YNRYLALY", +"IFVDGVPF", +"NLIDSYFV", +"RHTFSNYQ", +"TFSNYQHE", +"RHFDEGNC", +"CLCDRRAT", +"VVMAYITG", +"LRHFDEGN", +"DFGDFIQT", +"ITILDGIS", +"HFDEGNCD", +"FSNYQHEE", +"NLVVMAYI", +"ALCADSII", +"CDAMRNAG", +"TNNLVVMA", +"LVVMAYIT", +"LPTEVLTE", +"LREMLAHA", +"FKELLVYA", +"AESHVDTD", +"TCAKEIKE", +"SHVDTDLT", +"ESHVDTDL", +"CAKEIKES", +"MRNAGIVG", +"VMAYITGG", +"FDEGNCDT", +"LCADSIII", +"DGVPFVVS", +"FDRYFKYW", +"KDWYDFVE", +"TILDGISQ", +"MAYITGGV", +"HVDTDLTK", +"QLTSQWLT", +"CCDDDYFN", +"CDDDYFNK", +"DAMRNAGI", +"VDGVPFVV", +"NCDTLKEI", +"RYFKYWDQ", +"AMRNAGIV", +"YFVVKRHT", +"DEGNCDTL", +"SYFVVKRH", +"KKDWYDFV", +"EGNCDTLK", +"REMLAHAE", +"DRYFKYWD", +"CDTLKEIL", +"DSYFVVKR", +"NLREMLAH", +"PLGIDLDE", +"GNCDTLKE", +"FVDGVPFV", +"SSRLSFKE", +"LTSQWLTN", +"LSFKELLV", +"RKYKGIKI", +"YFNKKDWY", +"LHSSRLSF", +"DYFNKKDW", +"LKEILVTY", +"HSSRLSFK", +"DTLKEILV", +"NLHSSRLS", +"EMLAHAEE", +"VKRHTFSN", +"YLTSSSKT", +"RLSFKELL", +"VRKIFVDG", +"DNTSRYWE", +"VVKRHTFS", +"TSQWLTNI", +"YFKYWDQT", +"FNKKDWYD", +"ALRHFDEG", +"SFKELLVY", +"GYLTSSSK", +"DDDYFNKK", +"SQWLTNIF", +"KRHTFSNY", +"DDYFNKKD", +"NKKDWYDF", +"TLKEILVT", +"SNYQHEET", +"SRLSFKEL", +"IIIGGAKL", +"SVSSPDAV", +"FVVSTGYH", +"NGYLTSSS", +"VYALRHFD", +"PFVVSTGY", +"FVVKRHTF", +"YALRHFDE", +"ILVTYNCC", +"GGCVFSYV", +"LQKAAITI", +"EILVTYNC", +"VSVSSPDA", +"AITILDGI", +"VVSTGYHF", +"VPFVVSTG", +"AFGGCVFS", +"YHFRELGV", +"DVNLHSSR", +"DNLIDSYF", +"GVPFVVST", +"KEILVTYN", +"NCCDDDYF", +"VSTGYHFR", +"LKKSLNVA", +"QDVNLHSS", +"LVRKIFVD", +"DLDEWSMA", +"GYHFRELG", +"QKAAITIL", +"VNLHSSRL", +"LTSSSKTP", +"VTYNCCDD", +"LVTYNCCD", +"QHEETIYN", +"NPDILRVY", +"ATAQEAYE", +"TGYHFREL", +"KLKKSLNV", +"STGYHFRE", +"PDILRVYA", +"IAFGGCVF", +"RVLQKAAI", +"VLQKAAIT", +"YNCCDDDY", +"KSLNVAKS", +"KKSLNVAK", +"DSIIIGGA", +"TYNCCDDD", +"NYQHEETI", +"FATAQEAY", +"EHFIETIS", +"YQHEETIY", +"PLVRKIFV", +"TVKGLDYK", +"LQQLRVES", +"AVANGDSE", +"SIIIGGAK", +"HEETIYNL", +"EEHFIETI", +"KAAITILD", +"NTSRYWEP", +"NQDVNLHS", +"LVYALRHF", +"AAITILDG", +"LQPVSELL", +"DLVYALRH", +"ADSIIIGG", +"HNQDVNLH", +"QYNRYLAL", +"CADSIIIG", +"VANGDSEV", +"ELGTEVNE", +"EETIYNLL", +"TSSSKTPE", +"MLAHAEET", +"VSSPDAVT", +"FGGCVFSY", +"LGTEVNEF", +"TLQPVSEL", +"HFRELGVV", +"FRELGVVH", +"KTLQPVSE", +"NCVNCLDD", +"YNGYLTSS", +"SSSKTPEE", +"ADLVYALR", +"TTTLNDFN", +"TPLGIDLD", +"PNCVNCLD", +"YHPNCVNC", +"NGDSEVVL", +"ETVKGLDY", +"HPNCVNCL", +"ANGDSEVV", +"LDDRCILH", +"VETVKGLD", +"CLDDRCIL", +"VMVELVAE", +"FTTTLNDF", +"HVMVELVA", +"GHVMVELV", +"DDRCILHC", +"RTAPHGHV", +"SSKTPEEH", +"FVETVKGL", +"KKLKKSLN", +"GTEVNEFA", +"VSELLTPL", +"PEEHFIET", +"IYNLLKDC", +"SKTPEEHF", +"SYAAFATA", +"FKYWDQTY", +"LKKLKKSL", +"NCLDDRCI", +"GCVFSYVG", +"CVFSYVGC", +"RELGVVHN", +"CVNCLDDR", +"KYWDQTYH", +"AYNGYLTS", +"HGHVMVEL", +"VNCLDDRC", +"YAAFATAQ", +"GVVHNQDV", +"LGVVHNQD", +"ELGVVHNQ", +"RKLMPVCV", +"LTPLGIDL", +"QIGVVREF", +"SELLTPLG", +"HFIETISL", +"VVHNQDVN", +"VYLAVFDK", +"TAYNGYLT", +"AAFATAQE", +"YWDQTYHP", +"YAADPAMH", +"FSYVGCHN", +"VHNQDVNL", +"YNLLKDCP", +"VGGQIVTC", +"VFSYVGCH", +"VTAYNGYL", +"VLKKLKKS", +"TPEEHFIE", +"KTPEEHFI", +"YKNTCDGT", +"QPVSELLT", +"FIETISLA", +"ELLTPLGI", +"RQRLTKYT", +"LLTPLGID", +"HCANFNVL", +"DAVTAYNG", +"IGVVREFL", +"GDSEVVLK", +"PDAVTAYN", +"FGPLVRKI", +"APHGHVMV", +"VVLKKLKK", +"DWLEEKFK", +"AFATAQEA", +"EVVLKKLK", +"LDWLEEKF", +"CANFNVLF", +"LTKYTMAD", +"NTCDGTTF", +"NRFTTTLN", +"TAPHGHVM", +"SYVGCHNK", +"KYTMADLV", +"LHCANFNV", +"NEFACVVA", +"RCILHCAN", +"VYAADPAM", +"YTMADLVY", +"TKYTMADL", +"IETISLAG", +"NNAMQVES", +"KNTCDGTT", +"SEVVLKKL", +"PHGHVMVE", +"GPLVRKIF", +"ETIYNLLK", +"DRCILHCA", +"AVTAYNGY", +"PSYAAFAT", +"SPDAVTAY", +"TTLNDFNL", +"QTYHPNCV", +"TYHPNCVN", +"TMADLVYA", +"GVVREFLT", +"GCHNKCAY", +"CILHCANF", +"SSPDAVTA", +"SFGPLVRK", +"ILHCANFN", +"NLLKDCPA", +"DSEVVLKK", +"EVARDLSL", +"SLLMPILT", +"PVSELLTP", +"TIYNLLKD", +"MADLVYAL", +"GGQIVTCA", +"FLRDGWEI", +"MVELVAEL", +"QRLTKYTM", +"AKHDFFKF", +"DQTYHPNC", +"VYEKLKPV", +"LLKDCPAV", +"RFTTTLND", +"VNEFACVV", +"RLTKYTMA", +"LLMPILTL", +"GFDVEGCH", +"WDQTYHPN", +"YSLLMPIL", +"LPSYAAFA", +"CDGTTFTY", +"VAKHDFFK", +"VKGLDYKA", +"TSRYWEPE", +"HIQWMVMF", +"VMFTPLVP", +"IQWMVMFT", +"DYKAFKQI", +"LDYKAFKQ", +"NFNVLFST", +"YVGCHNKC", +"LEEKFKEG", +"AFVETVKG", +"FNVLFSTV", +"LAHAEETR", +"GLDYKAFK", +"QWMVMFTP", +"VGCHNKCA", +"KGLDYKAF", +"EFLRDGWE", +"TSFGPLVR", +"ETISLAGS", +"ADPAMHAA", +"YEKLKPVL", +"LMPILTLT", +"IKTLQPVS", +"LKVPATVS", +"AADPAMHA", +"MVMFTPLV", +"WIGFDVEG", +"WMVMFTPL", +"SLKVPATV", +"EEKFKEGV", +"CHNKCAYW", +"EKLKPVLD", +"EFACVVAD", +"NVLFSTVF", +"VLFSTVFP", +"VIKTLQPV", +"TSMKYFVK", +"VIHFGAGS", +"AVAKHDFF", +"EVNEFACV", +"ACVVADAV", +"TLNDFNLV", +"YLNTLTLA", +"YKDWSYSG", +"SAFVETVK", +"LTSMKYFV", +"ANFNVLFS", +"MRVIHFGA", +"KLMPVCVE", +"LNTLTLAV", +"WLEEKFKE", +"ELTSMKYF", +"MRSLKVPA", +"IGFDVEGC", +"LNIIPLTT", +"NMRVIHFG", +"VVADAVIK", +"RAWIGFDV", +"MPILTLTR", +"RVIHFGAG", +"AWIGFDVE", +"VRAWIGFD", +"FELTSMKY", +"HNKCAYWV", +"ESGLKTIL", +"NTLTLAVP", +"TLTRALTA", +"GSYKDWSY", +"MPVCVETK", +"TCDGTTFT", +"PLVPFWIT", +"LMPVCVET", +"NKCAYWVP", +"LTLAVPYN", +"FTPLVPFW", +"LTRALTAE", +"TIAFGGCV", +"TRALTAES", +"SYKDWSYS", +"LTLTRALT", +"RSLKVPAT", +"PILTLTRA", +"FACVVADA", +"HVRAWIGF", +"QQLRVESS", +"NESGLKTI", +"VEFLRDGW", +"TLTLAVPY", +"PAVAKHDF", +"LVPFWITI", +"AVPYNMRV", +"TPLVPFWI", +"LKPVLDWL", +"TLAVPYNM", +"KLKPVLDW", +"ILTLTRAL", +"TEVNEFAC", +"RALTAESH", +"MFTPLVPF", +"LAVPYNMR", +"NIIPLTTA", +"ALTAESHV", +"AGSYKDWS", +"VPYNMRVI", +"PYNMRVIH", +"CPAVAKHD", +"KPVLDWLE", +"LTAESHVD", +"YNMRVIHF", +"ADAVIKTL", +"VADAVIKT", +"DCPAVAKH", +"TAESHVDT", +"KCAYWVPR", +"PVCVETKA", +"ISLAGSYK", +"SLEIPRRN", +"LEIPRRNV", +"GFELTSMK", +"SLPSYAAF", +"RFYFYTSK", +"SHVVAFNT", +"GQIVTCAK", +"VLDWLEEK", +"TISLAGSY", +"SLAGSYKD", +"EKFKEGVE", +"VLLSVLQQ", +"QIVTCAKE", +"CVVADAVI", +"AFEKMVSL", +"EAFEKMVS", +"LLSVLQQL", +"KMVSLLSV", +"EKMVSLLS", +"PVLDWLEE", +"MVSLLSVL", +"LVSDIDIT", +"IHFGAGSD", +"TSAFVETV", +"TEAFEKMV", +"SLLSVLLS", +"QLRVESSS", +"VSLLSVLL", +"LLSVLLSM", +"STSAFVET", +"FLLPSLAT", +"CVPLNIIP", +"VCVETKAI", +"TILRKGGR", +"FEKMVSLL", +"QLHNDILL", +"CAYWVPRA", +"LAKDTTEA", +"LAGSYKDW", +"KTILRKGG", +"GCVPLNII", +"RTIAFGGC", +"ILLAKDTT", +"LKTILRKG", +"DAVIKTLQ", +"FDVEGCHA", +"AKDTTEAF", +"TTEAFEKM", +"LKDCPAVA", +"ASTSAFVE", +"LLAKDTTE", +"LNDFNLVA", +"SSLPSYAA", +"LLSMQGAV", +"IVTCAKEI", +"HNDILLAK", +"DTTEAFEK", +"KDTTEAFE", +"LSVLQQLR", +"FSSLPSYA", +"LHNDILLA", +"TLVSDIDI", +"SKEGFFTY", +"LLPSLATV", +"VVLLSVLQ", +"VTCAKEIK", +"EFSSLPSY", +"KDCPAVAK", +"NDILLAKD", +"TQYNRYLA", +"GLKTILRK", +"CVETKAIV", +"AHAEETRK", +"VEGCHATR", +"DILLAKDT", +"TLQAENVT", +"SVLQQLRV", +"FTYICGFI", +"GRTIAFGG", +"SVLLSMQG", +"DGCVPLNI", +"EGCHATRE", +"ATVAYFNM", +"KEGFFTYI", +"AVIKTLQP", +"DVEGCHAT", +"SEFSSLPS", +"QRKYKGIK", +"VLQQLRVE", +"EGFFTYIC", +"VETKAIVS", +"GCHATREA", +"VLLSMQGA", +"LRVESSSK", +"RVESSSKL", +"LQAIASEF", +"YICGFIQQ", +"LSMQGAVD", +"QAIASEFS", +"SGLKTILR", +"SKLWAQCV", +"SLNVAKSE", +"LPSLATVA", +"TYICGFIQ", +"FGAGSDKG", +"LNVAKSEF", +"NVAKSEFD", +"LSVLLSMQ", +"NRATLQAI", +"HFGAGSDK", +"ETKAIVST", +"RKGGRTIA", +"AIASEFSS", +"SMQGAVDI", +"GGRTIAFG", +"LATVAYFN", +"IASEFSSL", +"FTSLEIPR", +"MQGAVDIN", +"SLATVAYF", +"PSLATVAY", +"VSTIQRKY", +"TVAYFNMV", +"ILRKGGRT", +"TIQRKYKG", +"TLQAIASE", +"IQRKYKGI", +"RATLQAIA", +"QGAVDINK", +"ASEFSSLP", +"IVSTIQRK", +"LDNRATLQ", +"ATLQAIAS", +"VPFWITIA", +"TSLEIPRR", +"SQLMCQPI", +"DNRATLQA", +"VPLNIIPL", +"PFWITIAY", +"RHVRAWIG", +"LRKGGRTI", +"EIPRRNVA", +"TKAIVSTI", +"STIQRKYK", +"KGGRTIAF", +"HNESGLKT", +"ESSSKLWA", +"SVVLLSVL", +"SSSKLWAQ", +"KHDFFKFR", +"ATLVSDID", +"VESSSKLW", +"PRRNVATL", +"HGFELTSM", +"SSKLWAQC", +"VARDLSLQ", +"TTVDNINL", +"GAGSDKGV", +"FFTYICGF", +"CEEMLDNR", +"GFFTYICG", +"KAIVSTIQ", +"IPRRNVAT", +"PLNIIPLT", +"HDFFKFRI", +"KLWAQCVQ", +"WAQCVQLH", +"PVLQVRDV", +"LWAQCVQL", +"EVGPEHSL", +"AIVSTIQR", +"EEMLDNRA", +"PEHSLAEY", +"AQCVQLHN", +"VAKSEFDR", +"DFFKFRID", +"VGPEHSLA", +"VATLQAEN", +"AHGFELTS", +"AYWVPRAS", +"YWVPRASA", +"NDSKEGFF", +"FVSDADST", +"LQAENVTG", +"CHATREAV", +"KSEFDRDA", +"RDGCVPLN", +"ATLQAENV", +"AIRHVRAW", +"NSEVGPEH", +"QCVQLHND", +"ATREAVGT", +"VQLHNDIL", +"CVQLHNDI", +"IIPLTTAA", +"AGSDKGVA", +"NVATLQAE", +"DFVSDADS", +"SEVGPEHS", +"FFKFRIDG", +"LQFKRPIN", +"ENDSKEGF", +"SDADSTLI", +"AKSEFDRD", +"GPEHSLAE", +"PLTTAAKL", +"DSKEGFFT", +"DLIISDMY", +"IPLTTAAK", +"QAENVTGL", +"FKFRIDGD", +"AVLRQWLP", +"TAVLRQWL", +"EAIRHVRA", +"WVPRASAN", +"ARDGCVPL", +"EEAIRHVR", +"SATLVSDI", +"GSDKGVAP", +"EMLDNRAT", +"DADSTLIG", +"REEAIRHV", +"VSDADSTL", +"VKIYCPAC", +"YHNESGLK", +"SDKGVAPG", +"NDFVSDAD", +"FYFYTSKT", +"LIISDMYD", +"MLDNRATL", +"HATREAVG", +"KWDLIISD", +"YSQLMCQP", +"DKGVAPGT", +"LTTAAKLM", +"AENVTGLF", +"APGTAVLR", +"YFYTSKTT", +"HSLAEYHN", +"ANKWDLII", +"FRIDGDMV", +"NKWDLIIS", +"ARDLSLQF", +"KFRIDGDM", +"VAPGTAVL", +"AEYHNESG", +"GTAVLRQW", +"VDINKLCE", +"WDLIISDM", +"LNDFVSDA", +"RDLSLQFK", +"LAEYHNES", +"DINKLCEE", +"AVDINKLC", +"LLVDSDLN", +"GVAPGTAV", +"SLAEYHNE", +"KGVAPGTA", +"NNARDGCV", +"PGTAVLRQ", +"GAVDINKL", +"NAVVKIYC", +"TANKWDLI", +"SRYWEPEF", +"NARDGCVP", +"QNAVVKIY", +"GTLLVDSD", +"EHSLAEYH", +"CHNSEVGP", +"TLLVDSDL", +"VPRASANI", +"DLNDFVSD", +"LDNDALNN", +"EYHNESGL", +"KENDSKEG", +"DSTLIGDC", +"AVVKIYCP", +"ADSTLIGD", +"ENVTGLFK", +"TTAAKLMV", +"SLQFKRPI", +"IINNARDG", +"DLSLQFKR", +"TREEAIRH", +"NVTGLFKD", +"KLDNDALN", +"DSATLVSD", +"DNDALNNI", +"LNNIINNA", +"TKNVTKEN", +"HTANKWDL", +"TVDNINLH", +"SRQRLTKY", +"LSLQFKRP", +"NNIINNAR", +"MLRKLDND", +"VTGLFKDC", +"TMLRKLDN", +"KSKCEESS", +"HNSEVGPE", +"NIINNARD", +"SEFDRDAA", +"INNARDGC", +"WAHGFELT", +"FTMLRKLD", +"VHTANKWD", +"KFKEGVEF", +"ISRQRLTK", +"HISRQRLT", +"RKLDNDAL", +"PRASANIG", +"REAVGTNL", +"RNVATLQA", +"SDLNDFVS", +"LRKLDNDA", +"WLPTGTLL", +"PTGTLLVD", +"LPTGTLLV", +"DALNNIIN", +"TREAVGTN", +"RRNVATLQ", +"PHISRQRL", +"LVDSDLND", +"ALNNIINN", +"KTKNVTKE", +"NVYLAVFD", +"STLIGDCA", +"NDALNNII", +"VDSDLNDF", +"NTLNDLNE", +"NKLCEEML", +"TGTLLVDS", +"TGLFKDCS", +"KIYCPACH", +"INTLNDLN", +"DMVPHISR", +"VPHISRQR", +"GDMVPHIS", +"FKEGVEFL", +"TVHTANKW", +"MVPHISRQ", +"MMGFKMNY", +"LGYFCTCY", +"FYTSKTTV", +"MLVYCFLG", +"LVYCFLGY", +"SMMGFKMN", +"FLGYFCTC", +"DSDLNDFV", +"LINTLNDL", +"FDGKSKCE", +"GYFCTCYF", +"GKSKCEES", +"CATVHTAN", +"VLRQWLPT", +"IGDCATVH", +"PQNAVVKI", +"IISDMYDP", +"DGKSKCEE", +"TLIGDCAT", +"EAVGTNLP", +"LIGDCATV", +"INKLCEEM", +"KLCEEMLD", +"DCATVHTA", +"LCEEMLDN", +"DGDMVPHI", +"GDCATVHT", +"SDMYDPKT", +"ATVHTANK", +"VFDGKSKC", +"RIDGDMVP", +"TKENDSKE", +"IDGDMVPH", +"TLNDLNET", +"IMLVYCFL", +"IVFDGKSK", +"ISMMGFKM", +"VIVFDGKS", +"LISMMGFK", +"ISDMYDPK", +"YFCTCYFG", +"HPDSATLV", +"NVTKENDS", +"ANIGCNHT", +"CIMLVYCF", +"QWLPTGTL", +"HAEETRKL", +"GLFKDCSK", +"EFDRDAAM", +"LNDLNETL", +"INVIVFDG", +"VTKENDSK", +"RASANIGC", +"SLINTLND", +"ITREEAIR", +"NVIVFDGK", +"ACHNSEVG", +"GLHPTQAP", +"RLISMMGF", +"ASANIGCN", +"LWAHGFEL", +"TAAKLMVV", +"QCIMLVYC", +"PDSATLVS", +"TVASLINT", +"LRQWLPTG", +"AVGTNLPL", +"VVKIYCPA", +"VGTNLPLQ", +"TCYFGLFC", +"DMYDPKTK", +"NKLKAHKD", +"AEETRKLM", +"VLWAHGFE", +"YYSQLMCQ", +"KEGVEFLR", +"LHPTQAPT", +"MYDPKTKN", +"YDPKTKNV", +"TKEGATTC", +"KNVTKEND", +"GTNLPLQL", +"VASLINTL", +"KEGATTCG", +"CYFGLFCL", +"YFGLFCLL", +"FITREEAI", +"RQWLPTGT", +"PACHNSEV", +"LFKDCSKV", +"NLPLQLGF", +"ASLINTLN", +"TYRRLISM", +"TNLPLQLG", +"FVLWAHGF", +"LPLQLGFS", +"IYCPACHN", +"SANIGCNH", +"TQLCQYLN", +"FKDCSKVI", +"NIGCNHTG", +"MTYRRLIS", +"KLKAHKDK", +"PINVIVFD", +"PLQLGFST", +"RRLISMMG", +"TGLHPTQA", +"FGLFCLLN", +"DMTYRRLI", +"YCPACHNS", +"KDMTYRRL", +"MLSDTLKN", +"LPVLQVRD", +"QMLSDTLK", +"VYCFLGYF", +"QLCQYLNT", +"YRRLISMM", +"EETRKLMP", +"GVEFLRDG", +"EGVEFLRD", +"LQLGFSTG", +"FDRDAAMQ", +"KFKTEGLC", +"LPQNAVVK", +"LHPDSATL", +"SKVITGLH", +"FKTEGLCV", +"CYLATALL", +"CPACHNSE", +"DPKTKNVT", +"ERHSLSHF", +"KDCSKVIT", +"RHSLSHFV", +"PKTKNVTK", +"LSLPVLQV", +"DCSKVITG", +"QLGFSTGV", +"VYYSQLMC", +"NTDFSRVS", +"CSKVITGL", +"VYYGNALD", +"LIPLMYKG", +"GFKMNYQV", +"YTQLCQYL", +"TEGLCVDI", +"LPINVIVF", +"KTEGLCVD", +"HLIPLMYK", +"ITGLHPTQ", +"VVGEGSEG", +"APTHLSVD", +"DTLKNLSD", +"AAKLMVVI", +"TEETFKLS", +"SVYYSQLM", +"ISTKHFYW", +"VDNINLHT", +"STKHFYWF", +"LGFSTGVN", +"HPTQAPTH", +"QFKHLIPL", +"DRDAAMQR", +"FKLSYGIA", +"VFVLWAHG", +"EETFKLSY", +"CISTKHFY", +"TDFSRVSA", +"ICISTKHF", +"NNCYLATA", +"MGFKMNYQ", +"ASVYYSQL", +"YSHVVAFN", +"TQAPTHLS", +"KSASVYYS", +"SDTLKNLS", +"LSDTLKNL", +"RDAAMQRK", +"FKMNYQVN", +"PTQAPTHL", +"IICISTKH", +"TLKNLSDR", +"FKHLIPLM", +"DQFKHLIP", +"PGDQFKHL", +"ETFKLSYG", +"SASVYYSQ", +"GDQFKHLI", +"YIICISTK", +"SDRVVFVL", +"PTGYVDTP", +"FCTCYFGL", +"KHLIPLMY", +"QAPTHLSV", +"TKHFYWFF", +"ATEETFKL", +"CGYLPQNA", +"NCYLATAL", +"FSRVSAKP", +"VITGLHPT", +"KMNYQVNG", +"LQCIMLVY", +"CTCYFGLF", +"YMRSLKVP", +"PLTSFGPL", +"YLPQNAVV", +"GFSTGVNL", +"DRVVFVLW", +"FSTGVNLV", +"DFSRVSAK", +"MNYQVNGY", +"KVITGLHP", +"KGLPWNVV", +"ETRKLMPV", +"QLSLPVLQ", +"VFPLTSFG", +"YKGLPWNV", +"FPLTSFGP", +"RVVFVLWA", +"VVFVLWAH", +"TCGYLPQN", +"TLQCIMLV", +"SLPVLQVR", +"YERHSLSH", +"PPGDQFKH", +"VPTGYVDT", +"SRVSAKPP", +"LTSFGPLV", +"DAAMQRKL", +"NYQVNGYP", +"LMYKGLPW", +"LKNLSDRV", +"YQVNGYPN", +"TVFPLTSF", +"PLMYKGLP", +"KPPPGDQF", +"YCFLGYFC", +"TTVASLIN", +"YTSKTTVA", +"NTLQCIML", +"RVSAKPPP", +"VTVKNGSI", +"TFKLSYGI", +"VSAKPPPG", +"DNNCYLAT", +"LRANNTKG", +"IPLMYKGL", +"MYKGLPWN", +"EGATTCGY", +"SVTVKNGS", +"VHFISNSW", +"TYERHSLS", +"TTCGYLPQ", +"PPPGDQFK", +"MFITREEA", +"KTVQFCDA", +"KTTVASLI", +"GSIHLYFD", +"FSTVFPLT", +"LFSTVFPL", +"LDNLRANN", +"AKPPPGDQ", +"VKNGSIHL", +"SLPINVIV", +"LCQYLNTL", +"SAKPPPGD", +"IHLYFDKA", +"NGSIHLYF", +"CQYLNTLT", +"NLRANNTK", +"STVFPLTS", +"SKTTVASL", +"HLYFDKAG", +"VNLVAVPT", +"PWNVVRIK", +"NLVAVPTG", +"LVAVPTGY", +"WNVVRIKI", +"GYLPQNAV", +"GATTCGYL", +"TSKTTVAS", +"KNGSIHLY", +"QYLNTLTL", +"ATTCGYLP", +"STGVNLVA", +"CFLGYFCT", +"DNLRANNT", +"TVKNGSIH", +"LSDRVVFV", +"GVVGEGSE", +"ETFYPKLQ", +"NLSDRVVF", +"SIHLYFDK", +"TSVVLLSV", +"GSLPINVI", +"VETFYPKL", +"NLHPDSAT", +"LYFDKAGQ", +"GVNLVAVP", +"KYTQLCQY", +"KGSLPINV", +"VAVPTGYV", +"KNLSDRVV", +"GNLHPDSA", +"NMFITREE", +"VQLSLPVL", +"EGLCVDIP", +"AVPTGYVD", +"AKLMVVIP", +"TFYPKLQS", +"HVQLSLPV", +"NDFNLVAM", +"TGVNLVAV", +"THVQLSLP", +"DFNLVAMK", +"RVVFNGVS", +"CTSVVLLS", +"TKKAGGTT", +"KKAGGTTE", +"TVQFCDAM", +"LMVVIPDY", +"NTKGSLPI", +"RRVVFNGV", +"PTKKAGGT", +"KLSYGIAT", +"RSDVLLPL", +"TKGSLPIN", +"NNTKGSLP", +"TEISFMLW", +"FYPKLQSS", +"AGGTTEML", +"GLPWNVVR", +"EISFMLWC", +"SVTSNYSG", +"LPWNVVRI", +"YTEISFML", +"KLMVVIPD", +"ISVTSNYS", +"KTYERHSL", +"KATEETFK", +"FMLWCKDG", +"ISFMLWCK", +"AAMQRKLE", +"PNMFITRE", +"QVNGYPNM", +"VQFCDAMR", +"VNGYPNMF", +"QKTYERHS", +"IISVTSNY", +"RYMRSLKV", +"YPKLQSSQ", +"FCDAMRNA", +"GLCVDIPG", +"KAGGTTEM", +"GYPNMFIT", +"AYIICIST", +"DYTEISFM", +"NGYPNMFI", +"YPNMFITR", +"IPTKKAGG", +"IAYIICIS", +"QFCDAMRN", +"VIPTKKAG", +"TIAYIICI", +"WITIAYII", +"SFMLWCKD", +"IDYTEISF", +"ITIAYIIC", +"GVSFSTFE", +"NGVSFSTF", +"YFDKAGQK", +"TRKLMPVC", +"PKLQSSQA", +"LSYGIATV", +"KLQSSQAW", +"SFSTFEEA", +"HVETFYPK", +"VTSNYSGV", +"VSFSTFEE", +"FNGVSFST", +"FDKAGQKT", +"KCTSVVLL", +"GHVETFYP", +"LQSSQAWQ", +"DGHVETFY", +"SALVYDNK", +"GQKTYERH", +"VQMLSDTL", +"LCVDIPGI", +"LKATEETF", +"ALVYDNKL", +"ARYMRSLK", +"MWALIISV", +"GLFCLLNR", +"KDGHVETF", +"NGNLHPDS", +"ANNTKGSL", +"VVFNGVSF", +"DKAGQKTY", +"ALIISVTS", +"SMWALIIS", +"LIISVTSN", +"WALIISVT", +"KAGQKTYE", +"CKDGHVET", +"FWITIAYI", +"LFCLLNRY", +"INGNLHPD", +"CLLNRYFR", +"AGQKTYER", +"HSLSHFVN", +"PKDMTYRR", +"FNLVAMKY", +"VYDNKLKA", +"FCLLNRYF", +"IVQMLSDT", +"LVYDNKLK", +"SDVKCTSV", +"VKCTSVVL", +"DVKCTSVV", +"AQEAYEQA", +"VFNGVSFS", +"YEQAVANG", +"TSNYSGVV", +"IPKDMTYR", +"NLVAMKYN", +"IFFITGNT", +"IDINGNLH", +"DINGNLHP", +"VAMKYNYE", +"IKVATVQS", +"RPPLNRNY", +"PCIKVATV", +"LTQYNRYL", +"TAQEAYEQ", +"SNYSGVVT", +"DNKLKAHK", +"PINPTDQS", +"YIDINGNL", +"YDNKLKAH", +"KVATVQSK", +"GGKPCIKV", +"FKLNIKLL", +"GKPCIKVA", +"EAYEQAVA", +"QEAYEQAV", +"EQAVANGD", +"SLSHFVNL", +"KPCIKVAT", +"RANNTKGS", +"KLNIKLLG", +"VVIPTKKA", +"LVAMKYNY", +"QAVANGDS", +"AFKLNIKL", +"MSDVKCTS", +"AYEQAVAN", +"FKRPINPT", +"WCKDGHVE", +"SKMSDVKC", +"MLWCKDGH", +"KMSDVKCT", +"KIVQMLSD", +"LWCKDGHV", +"RPINPTDQ", +"YYGNALDQ", +"QSKMSDVK", +"CIKVATVQ", +"KRPINPTD", +"VQSKMSDV", +"VATVQSKM", +"YGNALDQA", +"DAFKLNIK", +"TVQSKMSD", +"VYDYLVST", +"KYNYEPLT", +"NPTDQSSY", +"ATVQSKMS", +"IDAFKLNI", +"IKIVQMLS", +"YDYLVSTQ", +"TQEFRYMN", +"LSHFVNLD", +"LKVGGSCV", +"INPTDQSS", +"VSTQEFRY", +"FVNLDNLR", +"QEFRYMNS", +"PTDQSSYI", +"MKYNYEPL", +"STQEFRYM", +"SIDAFKLN", +"ITGNTLQC", +"PLTQYNRY", +"AARYMRSL", +"GIPKDMTY", +"LVSTQEFR", +"VPGFNEKT", +"NSIDAFKL", +"PIFFITGN", +"DYLVSTQE", +"YLVSTQEF", +"TDQSSYIV", +"SHFVNLDN", +"TGVVGEGS", +"HFVNLDNL", +"PGFNEKTH", +"FRYMNSQG", +"EFRYMNSQ", +"NLDNLRAN", +"HTGVVGEG", +"CNHTGVVG", +"RIKIVQML", +"TGNTLQCI", +"LPLTQYNR", +"VVRIKIVQ", +"QFKRPINP", +"VVREFLTR", +"GNTLQCIM", +"NEKTHVQL", +"RYMNSQGL", +"FITGNTLQ", +"FNEKTHVQ", +"KNSIDAFK", +"GFNEKTHV", +"VRIKIVQM", +"PKNSIDAF", +"PPKNSIDA", +"NHTGVVGE", +"KVGGSCVL", +"KTHVQLSL", +"ESLVPGFN", +"LVPGFNEK", +"EKTHVQLS", +"SLVPGFNE", +"PISFPLCA", +"VMFLARGI", +"VNLDNLRA", +"MFLARGIV", +"FFITGNTL", +"NVVRIKIV", +"VREFLTRN", +"AMKYNYEP", +"LLNRYFRL", +"GCNHTGVV", +"PRPPLNRN", +"LNRYFRLT", +"LKSAYENF", +"PLKVGGSC", +"NGPLKVGG", +"KSAYENFN", +"NAMQVESD", +"EYSHVVAF", +"SDVLLPLT", +"ISFPLCAN", +"DVLLPLTQ", +"GPLKVGGS", +"EVLSDREL", +"AISMWALI", +"VDIPGIPK", +"DIPGIPKD", +"PGIPKDMT", +"LLPLTQYN", +"RYFRLTLG", +"SVDTKFKT", +"LSVDTKFK", +"VGGSCVLS", +"VLLPLTQY", +"LLKSAYEN", +"CVDIPGIP", +"ISMWALII", +"PLLSAGIF", +"VDTKFKTE", +"IPGIPKDM", +"QLLKSAYE", +"IQLLKSAY", +"NYLKRRVV", +"IGCNHTGV", +"LKRRVVFN", +"KRRVVFNG", +"YLKRRVVF", +"NRYFRLTL", +"VREVLSDR", +"TKFKTEGL", +"DTKFKTEG", +"LAKHCLHV", +"SAYENFNQ", +"KHFYWFFS", +"VNKGEDIQ", +"NVNKGEDI", +"LLSAGIFG", +"KGEDIQLL", +"HFYWFFSN", +"GEDIQLLK", +"NKGEDIQL", +"NLAKHCLH", +"REVLSDRE", +"LSAGIFGA", +"YWFFSNYL", +"GGSCVLSG", +"FYWFFSNY", +"SYGIATVR", +"AMQVESDD", +"YGIATVRE", +"PTHLSVDT", +"YYFMRFRR", +"HVVGPNVN", +"TRVECTTI", +"LHVVGPNV", +"ATRVECTT", +"THLSVDTK", +"RATRVECT", +"AKHCLHVV", +"HLSVDTKF", +"GEYSHVVA", +"ADNNCYLA", +"GIVFMCVE", +"IKLLGVGG", +"GNALDQAI", +"IVFMCVEY", +"NRATRVEC", +"RNRATRVE", +"YKRNRATR", +"LNIKLLGV", +"KRNRATRV", +"NIKLLGVG", +"NALDQAIS", +"ATVREVLS", +"DIQLLKSA", +"YENFNQHE", +"TNGPLKVG", +"LLGVGGKP", +"LQFTSLEI", +"QFTSLEIP", +"REFLTRNP", +"YFMRFRRA", +"MCYKRNRA", +"DKLQFTSL", +"PNVNKGED", +"KLQFTSLE", +"CYKRNRAT", +"KLLGVGGK", +"YDKLQFTS", +"CLHVVGPN", +"MMCYKRNR", +"AYENFNQH", +"EDIQLLKS", +"ATNGPLKV", +"CMMCYKRN", +"EEAARYMR", +"RGIVFMCV", +"ALDQAISM", +"VGGKPCIK", +"TVREVLSD", +"SFPLCANG", +"LGVGGKPC", +"GIATVREV", +"VVGPNVNK", +"YFRLTLGV", +"LGVYDYLV", +"SSTCMMCY", +"GVYDYLVS", +"GVGGKPCI", +"MQVESDDY", +"KHCLHVVG", +"LVTMPLGY", +"VLSGHNLA", +"LAPLLSAG", +"TCMMCYKR", +"VTMPLGYV", +"SCVLSGHN", +"FRLTLGVY", +"LLPPKNSI", +"VLLAPLLS", +"ICGFIQQK", +"IATVREVL", +"GSCVLSGH", +"GPNVNKGE", +"CVLSGHNL", +"DQAISMWA", +"LDQAISMW", +"CGFIQQKL", +"NSQGLLPP", +"VGPNVNKG", +"YMNSQGLL", +"WKYPQVNG", +"TMPLGYVT", +"APLLSAGI", +"LYDKLQFT", +"ETLVTMPL", +"ENFNQHEV", +"LSGHNLAK", +"HNLAKHCL", +"GLLPPKNS", +"HCLHVVGP", +"TLVTMPLG", +"MNSQGLLP", +"KCEESSAK", +"YPTLNISD", +"LPPKNSID", +"SKCEESSA", +"STCMMCYK", +"LARGIVFM", +"GFIQQKLA", +"AKYTQLCQ", +"ARGIVFMC", +"SQGLLPPK", +"RLTLGVYD", +"DEFSSNVA", +"PLGYVTHG", +"MPLGYVTH", +"LLAPLLSA", +"QGLLPPKN", +"NETLVTMP", +"NFNQHEVL", +"FNQHEVLL", +"TLNISDEF", +"CEESSAKS", +"SGHNLAKH", +"SAGIFGAD", +"TLGVYDYL", +"LGYVTHGL", +"ERLKLFAA", +"PTLNISDE", +"LNISDEFS", +"VAKYTQLC", +"LNETLVTM", +"YIATNGPL", +"LKLFAAET", +"LTLGVYDY", +"RLKLFAAE", +"IATNGPLK", +"VFMCVEYC", +"GYVTHGLN", +"SDEFSSNV", +"FLARGIVF", +"GHNLAKHC", +"FMRFRRAF", +"YVTHGLNL", +"FMCVEYCP", +"ISDEFSSN", +"NISDEFSS", +"YCPIFFIT", +"CPIFFITG", +"VTHGLNLE", +"DLNETLVT", +"NDLNETLV", +"FIQQKLAL", +"HGLNLEEA", +"MCVEYCPI", +"EVLLAPLL", +"CVEYCPIF", +"QAISMWAL", +"NQHEVLLA", +"GDYILANT", +"EYCPIFFI", +"DYILANTC", +"EAARYMRS", +"DYIATNGP", +"EESSAKSA", +"EFLTRNPA", +"MRFRRAFG", +"ETLKATEE", +"QVESDDYI", +"NAGDYILA", +"VEYCPIFF", +"FAAETLKA", +"VESDDYIA", +"HEVLLAPL", +"AGDYILAN", +"ESSAKSAS", +"QHEVLLAP", +"SSAKSASV", +"FGEYSHVV", +"AETLKATE", +"GLNLEEAA", +"AKSASVYY", +"AAETLKAT", +"SAKSASVY", +"THGLNLEE", +"FNAIATCD", +"KLFAAETL", +"TLKATEET", +"NAIATCDW", +"TDFNAIAT", +"DFNAIATC", +"ESDDYIAT", +"AIATCDWT", +"TNAGDYIL", +"LNLEEAAR", +"LFAAETLK", +"VTDFNAIA", +"RRAFGEYS", +"VFGLYKNT", +"KPRPPLNR", +"FRRAFGEY", +"SDDYIATN", +"GKPRPPLN", +"AFGEYSHV", +"WTNAGDYI", +"DDYIATNG", +"RFRRAFGE", +"DNVTDFNA", +"GDFIQTTP", +"TERLKLFA", +"DWTNAGDY", +"FGLYKNTC", +"PGSGVPVV", +"IATCDWTN", +"DFIQTTPG", +"TTPGSGVP", +"TPGSGVPV", +"CTERLKLF", +"RAFGEYSH", +"VLSDRELH", +"SYYCKSHK", +"TCDWTNAG", +"QTTPGSGV", +"CDWTNAGD", +"FIQTTPGS", +"IQTTPGSG", +"NVTDFNAI", +"GIFGADPI", +"RYWEPEFY", +"ATCDWTNA", +"NVAKYTQL", +"YILANTCT", +"LEEAARYM", +"LSDRELHL", +"GLYKNTCV", +"LRVYANLG", +"KYPQVNGL", +"TCTERLKL", +"MMNVAKYT", +"ILRVYANL", +"AGIFGADP", +"MNVAKYTQ", +"NLEEAARY", +"ILANTCTE", +"WFFSNYLK", +"LYKNTCVG", +"NTCTERLK", +"WEVGKPRP", +"ANTCTERL", +"YPQVNGLT", +"SDRELHLS", +"SDNVTDFN", +"IMMNVAKY", +"EVGKPRPP", +"DRELHLSW", +"GSDNVTDF", +"YKNTCVGS", +"RELHLSWE", +"LANTCTER", +"GIMMNVAK", +"LSWEVGKP", +"CVGSDNVT", +"ADVTKIKP", +"VGSDNVTD", +"VYANLGER", +"YANLGERV", +"FFSNYLKR", +"SWEVGKPR", +"FSNYLKRR", +"NTCVGSDN", +"SNYLKRRV", +"KNTCVGSD", +"MSYYCKSH", +"TCVGSDNV", +"VGKPRPPL", +"GADVTKIK", +"ANLGERVR", +"RVYANLGE", +"DVTKIKPH", +"DGADVTKI", +"NLGERVRQ", +"NYSGVVTT", +"KGIMMNVA", +"YSGVVTTV", +"VTKIKPHN", +"PKGIMMNV", +"GDLQPLEQ", +"IHSLRVCV", +"LHLSWEVG", +"ELHLSWEV", +"LPKGIMMN", +"IFGADPIH", +"QALLKTVQ", +"RQALLKTV", +"VRQALLKT", +"FGADPIHS", +"DLYDKLQF", +"SGVVTTVM", +"RVRQALLK", +"KCDLQNYG", +"HSLRVCVD", +"LGERVRQA", +"TVMFLARG", +"GERVRQAL", +"LEKCDLQN", +"ERVRQALL", +"SSQAWQPG", +"WADNNCYL", +"TTVMFLAR", +"GMSYYCKS", +"EKCDLQNY", +"QSSQAWQP", +"DLQPLEQP", +"GVVTTVMF", +"VTTVMFLA", +"GADPIHSL", +"PNLYKMQR", +"LLEKCDLQ", +"YKMQRMLL", +"MPNLYKMQ", +"SLRVCVDT", +"VVTTVMFL", +"CDLQNYGD", +"DLQNYGDS", +"LYKMQRML", +"KMQRMLLE", +"AMPNLYKM", +"DDNLIDSY", +"MQRMLLEK", +"SQAWQPGV", +"QRMLLEKC", +"QAWQPGVA", +"NLYKMQRM", +"MLLEKCDL", +"VAMPNLYK", +"RMLLEKCD", +"ADPIHSLR", +"LQPLEQPT", +"GVAMPNLY", +"LQNYGDSA", +"WQPGVAMP", +"QPGVAMPN", +"LGRYMSAL", +"AWQPGVAM", +"PGVAMPNL", +"GRYMSALN", +"GGMSYYCK", +"TLPKGIMM", +"YMSALNHT", +"LGGMSYYC", +"RYMSALNH", +"KWADNNCY", +"EDDNLIDS", +"MSALNHTK", +"HLSWEVGK", +"ATLPKGIM", +"PIHSLRVC", +"DPIHSLRV", +"DEDDNLID", +"STKPVETS", +"TKPVETSN", +"SATLPKGI", +"KPVETSNS", +"YGDSATLP", +"LDGADVTK", +"ETSNSFDV", +"TSNSFDVL", +"GDSATLPK", +"NYGDSATL", +"DSATLPKG", +"KWKYPQVN", +"VETSNSFD", +"PVETSNSF", +"QNYGDSAT", +"TIQTIVEV", +"SNSFDVLK", +"SALNHTKK", +"SASIVAGG", +"KKWKYPQV", +"NSFDVLKS", +"ALNHTKKW", +"TTIQTIVE", +"LNHTKKWK", +"NHTKKWKY", +"SFDVLKSE", +"TKKWKYPQ", +"YLGGMSYY", +"HTKKWKYP", +"YLDGADVT", +"KPANNSLK", +"PANNSLKI", +"DNINLHTQ", +"KDEDDNLI", +"TTTIQTIV", +"TKIKPHNS", +"HEGKTFYV", +"TYLDGADV", +"VNLLTNMF", +"LKTVQFCD", +"KIKPHNSH", +"GPTYLDGA", +"FGPTYLDG", +"LLKTVQFC", +"DAVNLLTN", +"PTYLDGAD", +"QFGPTYLD", +"NLLTNMFT", +"AVNLLTNM", +"IKPHNSHE", +"EGKTFYVL", +"LRVCVDTV", +"ANNSLKIT", +"TNVYLAVF", +"KPHNSHEG", +"YGQQFGPT", +"EKDEDDNL", +"TNMFTPLI", +"LLTNMFTP", +"SHEGKTFY", +"GKTFYVLP", +"TFYVLPND", +"NMFTPLIQ", +"PHNSHEGK", +"MFTPLIQP", +"QQFGPTYL", +"KTFYVLPN", +"MTYGQQFG", +"LTNMFTPL", +"HNSHEGKT", +"RDLYDKLQ", +"SSYIVDSV", +"GQQFGPTY", +"DQSSYIVD", +"YIVDSVTV", +"TYGQQFGP", +"DSVTVKNG", +"QSSYIVDS", +"NSHEGKTF", +"ALLKTVQF", +"SYIVDSVT", +"VDSVTVKN", +"SSTFNVPM", +"STFNVPME", +"SMTYGQQF", +"NINLHTQV", +"FYVLPNDD", +"FLTRNPAW", +"IVDSVTVK", +"AYYFMRFR", +"EAFEYYHT", +"AFEYYHTT", +"RVCVDTVR", +"YVLPNDDT", +"MEKLKTLV", +"TSITSAVL", +"LYLGGMSY", +"TFNVPMEK", +"PMEKLKTL", +"VEAFEYYH", +"VDTVRTNV", +"VCVDTVRT", +"CLAYYFMR", +"LAYYFMRF", +"FEYYHTTD", +"FNVPMEKL", +"CVDTVRTN", +"FTLLLQLC", +"FFTLLLQL", +"VLPNDDTL", +"DTVRTNVY", +"MSMTYGQQ", +"LLLQLCTF", +"TLLLQLCT", +"PYFFTLLL", +"YFFTLLLQ", +"RTNVYLAV", +"QTTTIQTI", +"YMPYFFTL", +"QPPQTSIT", +"DTLRVEAF", +"NVPMEKLK", +"MPYFFTLL", +"LRVEAFEY", +"RVEAFEYY", +"VPMEKLKT", +"YQPPQTSI", +"DDTLRVEA", +"NYMPYFFT", +"DMSMTYGQ", +"TLRVEAFE", +"VLYQPPQT", +"LPNDDTLR", +"CTFTRSTN", +"PPQTSITS", +"PQTSITSA", +"LYQPPQTS", +"TCLAYYFM", +"QTSITSAV", +"TFTRSTNS", +"YWEPEFYE", +"PNDDTLRV", +"LLQLCTFT", +"INLHTQVV", +"LCTFTRST", +"NDDTLRVE", +"LQLCTFTR", +"QLCTFTRS", +"VDMSMTYG", +"TNYMPYFF", +"VELGTEVN", +"SQTSLRCG", +"IILKPANN", +"VRTNVYLA", +"CNSQTSLR", +"VLCNSQTS", +"VGDIILKP", +"QEKDEDDN", +"VTCLAYYF", +"TVRTNVYL", +"GDIILKPA", +"NNSLKITE", +"WEPEFYEA", +"TVELGTEV", +"DIILKPAN", +"NSQTSLRC", +"YTVELGTE", +"IKWADNNC", +"CVLCNSQT", +"VVDMSMTY", +"FTRSTNSR", +"GACVLCNS", +"VGACVLCN", +"VVGDIILK", +"RSTNSRIK", +"AGGIVAIV", +"FPLCANGQ", +"DRDLYDKL", +"ACVLCNSQ", +"STNSRIKA", +"LCNSQTSL", +"VVTCLAYY", +"NSLKITEE", +"TRSTNSRI", +"MESLVPGF", +"SLKITEEV", +"LKPANNSL", +"IVAGGIVA", +"CTNYMPYF", +"VFISPYNS", +"VAGGIVAI", +"LKITEEVG", +"QTSLRCGA", +"AVGACVLC", +"VCTNYMPY", +"SIVAGGIV", +"NLHTQVVD", +"VAIVVTCL", +"GGIVAIVV", +"IVVTCLAY", +"IVAIVVTC", +"AIVVTCLA", +"LQAVGACV", +"ISPYNSQN", +"FISPYNSQ", +"NVAITRAK", +"GIVAIVVT", +"ILKPANNS", +"PLCANGQV", +"ASIVAGGI", +"TSLRCGAC", +"TVDSSQGS", +"VDSSQGSE", +"FNVAITRA", +"NRVCTNYM", +"QTVDSSQG", +"QAVGACVL", +"RVCTNYMP", +"ISASIVAG", +"LNRVCTNY", +"LHTQVVDM", +"SDRDLYDK", +"VLQAVGAC", +"DISASIVA", +"TNSRIKAS", +"PYNSQNAV", +"KITEEVGH", +"HTVLQAVG", +"QVFGLYKN", +"RFNVAITR", +"ALDISASI", +"SLRCGACI", +"LDISASIV", +"YNSQNAVA", +"NQTTTIQT", +"AITRAKVG", +"TVLQAVGA", +"SPYNSQNA", +"AKVGILCI", +"VAITRAKV", +"TQTVDSSQ", +"ITRAKVGI", +"GILCIMSD", +"NRFNVAIT", +"SQNAVASK", +"NSQNAVAS", +"QNAVASKI", +"TRAKVGIL", +"GALDISAS", +"PHTVLQAV", +"RAKVGILC", +"DSSQGSEY", +"HTQVVDMS", +"YTPHTVLQ", +"EEVVENPT", +"VGILCIMS", +"GQVFGLYK", +"QLYLGGMS", +"LRCGACIR", +"EDNQTTTI", +"DNQTTTIQ", +"SEDNQTTT", +"TPHTVLQA", +"MYTPHTVL", +"NAVASKIL", +"SEEVVENP", +"LTRNPAWR", +"AVASKILG", +"KVGILCIM", +"EVVENPTI", +"VSEEVVEN", +"VVENPTIQ", +"VNRFNVAI", +"PLIQPIGA", +"NVNRFNVA", +"TPLIQPIG", +"ITEEVGHT", +"AVFISPYN", +"ILCIMSDR", +"LIQPIGAL", +"VASKILGL", +"FTPLIQPI", +"LCIMSDRD", +"SSQGSEYD", +"IGALDISA", +"TQVVDMSM", +"TQLYLGGM", +"CANGQVFG", +"PIGALDIS", +"CLNRVCTN", +"MSDRDLYD", +"QPLEQPTS", +"LCANGQVF", +"RCGACIRR", +"IQPIGALD", +"VTRCLNRV", +"CGACIRRP", +"NGQVFGLY", +"QVVDMSMT", +"TRCLNRVC", +"KAVFISPY", +"CNVNRFNV", +"GACIRRPF", +"ANGQVFGL", +"SCNVNRFN", +"AMYTPHTV", +"QPIGALDI", +"RCLNRVCT", +"EPEFYEAM", +"TRNPAWRK", +"RKAVFISP", +"IMSDRDLY", +"PTQTVDSS", +"FDVLKSED", +"EFYEAMYT", +"DVTQLYLG", +"DNLACEDL", +"PEFYEAMY", +"EDAQGMDN", +"LKSEDAQG", +"VTQLYLGG", +"QGSEYDYV", +"SEDAQGMD", +"DAQGMDNL", +"ASKILGLP", +"SQGSEYDY", +"WRKAVFIS", +"TDVTQLYL", +"CIMSDRDL", +"HSCNVNRF", +"MDNLACED", +"KSEDAQGM", +"FYEAMYTP", +"SKILGLPT", +"ILGLPTQT", +"DVLKSEDA", +"VLKSEDAQ", +"GMDNLACE", +"YEAMYTPH", +"LPTQTVDS", +"IVTRCLNR", +"EAMYTPHT", +"KILGLPTQ", +"QGMDNLAC", +"AQGMDNLA", +"TTEVVGDI", +"AYTVELGT", +"GLPTQTVD", +"AWRKAVFI", +"LGLPTQTV", +"KTTEVVGD", +"TETAHSCN", +"TTETAHSC", +"YYHTTDPS", +"GSEYDYVI", +"EYYHTTDP", +"VKTTEVVG", +"ECNVKTTE", +"QTTETAHS", +"CNVKTTEV", +"NVKTTEVV", +"AHSCNVNR", +"IFTQTTET", +"TAHSCNVN", +"ETAHSCNV", +"DVLECNVK", +"EVVGDIIL", +"NIVTRCLN", +"VIFTQTTE", +"TQTTETAH", +"FTQTTETA", +"VLECNVKT", +"PQVNGLTS", +"YVIFTQTT", +"LECNVKTT", +"TEVVGDII", +"VNGLTSIK", +"SEYDYVIF", +"SIKWADNN", +"QVNGLTSI", +"DYVIFTQT", +"NGLTSIKW", +"EYDYVIFT", +"YDYVIFTQ", +"TSIKWADN", +"TNIVTRCL", +"GLTSIKWA", +"TIKKPNEL", +"LTSIKWAD", +"IKKPNELS", +"PAWRKAVF", +"RNPAWRKA", +"LTIKKPNE", +"SLTIKKPN", +"VENPTIQK", +"NPAWRKAV", +"KKPNELSR", +"ENPTIQKD", +"KPNELSRV", +"PNELSRVL", +"SSLTIKKP", +"NSSLTIKK", +"EEVGHTDL", +"TTNIVTRC", +"STTTNIVT", +"TTTNIVTR", +"PLEQPTSE", +"TEEVGHTD", +"NPTIQKDV", +"FAKFLKTN", +"TIQKDVLE", +"GFAKFLKT", +"AGFAKFLK", +"VDNSSLTI", +"IQKDVLEC", +"QKDVLECN", +"PTIQKDVL", +"AKFLKTNC", +"DNSSLTIK", +"AYVDNSSL", +"YVDNSSLT", +"AAYVDNSS", +"VAGFAKFL", +"KDVLECNV", +"KVAGFAKF", +"NDKVAGFA", +"YNDKVAGF", +"DKVAGFAK", +"ISTSHKLV", +"STSHKLVL", +"DIYNDKVA", +"PFLCCKCC", +"LMAAYVDN", +"DLMAAYVD", +"AFDIYNDK", +"IYNDKVAG", +"TSHKLVLS", +"NELSRVLG", +"RAFDIYND", +"SHKLVLSV", +"FDIYNDKV", +"SFLGRYMS", +"TDLMAAYV", +"ELSRVLGL", +"VISTSHKL", +"MAAYVDNS", +"HVISTSHK", +"YRAFDIYN", +"LCCKCCYD", +"LSRVLGLK", +"CIRRPFLC", +"FLCCKCCY", +"EVGHTDLM", +"DHVISTSH", +"RPFLCCKC", +"YDHVISTS", +"CKCCYDHV", +"RRPFLCCK", +"CCKCCYDH", +"SRVLGLKT", +"CYDHVIST", +"VGHTDLMA", +"IRRPFLCC", +"HKLVLSVN", +"VSTTTNIV", +"HTDLMAAY", +"CCYDHVIS", +"FLGRYMSA", +"GHTDLMAA", +"KVVSTTTN", +"FLNKVVST", +"KCCYDHVI", +"VVSTTTNI", +"RVLGLKTL", +"NKVVSTTT", +"LNKVVSTT", +"ACIRRPFL", +"WDTIANYA", +"AKPFLNKV", +"DTIANYAK", +"VPWDTIAN", +"NSVPWDTI", +"VNSVPWDT", +"YAKPFLNK", +"NYAKPFLN", +"KPFLNKVV", +"PWDTIANY", +"AVNSVPWD", +"FQEKDEDD", +"LEQPTSEA", +"VLGLKTLA", +"LGLKTLAT", +"PFLNKVVS", +"SVPWDTIA", +"IANYAKPF", +"ANYAKPFL", +"TIANYAKP", +"KLVLSVNP", +"GCDVTDVT", +"CDVTDVTQ", +"DVTDVTQL", +"VTDVTQLY", +"GLKTLATH", +"PSFLGRYM", +"TDPSFLGR", +"DPSFLGRY", +"TTDPSFLG", +"RFQEKDED", +"LENVAFNV", +"ENVAFNVV", +"HTTDPSFL", +"VNPYVCNA", +"SVNPYVCN", +"NVAFNVVN", +"HKPPISFP", +"CRFQEKDE", +"SLENVAFN", +"KFLKTNCC", +"YCKSHKPP", +"LQSLENVA", +"QSLENVAF", +"FLKTNCCR", +"LSVNPYVC", +"YHTTDPSF", +"KTNCCRFQ", +"TNCCRFQE", +"LKTNCCRF", +"CKSHKPPI", +"LVLSVNPY", +"VLSVNPYV", +"RLQSLENV", +"SHKPPISF", +"KSHKPPIS", +"CCRFQEKD", +"NCCRFQEK", +"TRLQSLEN", +"KPPISFPL", +"FTRLQSLE", +"APGCDVTD", +"PGCDVTDV", +"NAPGCDVT", +"CNAPGCDV", +"VCNAPGCD", +"YVCNAPGC", +"FKLKDCVM", +"GFKLKDCV", +"SGFKLKDC", +"SAYTVELG", +"LSGFKLKD", +"SLSGFKLK", +"DMVDTSLS", +"VDTSLSGF", +"TSLSGFKL", +"MVDTSLSG", +"DTSLSGFK", +"YTPSKLIE", +"PYVCNAPG", +"NPYVCNAP", +"FSAVGNIC", +"PRVFSAVG", +"FLPRVFSA", +"VFSAVGNI", +"RVFSAVGN", +"LPRVFSAV", +"CYTPSKLI", +"ICYTPSKL", +"YYCKSHKP", +"NICYTPSK", +"PPISFPLC", +"IYTSGSTG", +"GNICYTPS", +"RHGTCERS", +"HGTCERSE", +"TPSKLIEY", +"CRHGTCER", +"PSKLIEYT", +"VGNICYTP", +"AVGNICYT", +"SAVGNICY", +"YCRHGTCE", +"EQPTSEAV", +"MLLEIKDT", +"LMLLEIKD", +"LLEIKDTE", +"LEIKDTEK", +"QPTSEAVE", +"PTSEAVEA", +"GLMLLEIK", +"EYCRHGTC", +"NGLMLLEI", +"TSACVLAA", +"KLIEYTDF", +"NLACEDLK", +"INGLMLLE", +"SKLIEYTD", +"LIEYTDFA", +"ATSACVLA", +"NSRIKASM", +"YTDFATSA", +"FATSACVL", +"TDFATSAC", +"CVLAAECT", +"ACVLAAEC", +"DFATSACV", +"VLMDGSII", +"SACVLAAE", +"EYTDFATS", +"IEYTDFAT", +"LMDGSIIQ", +"MDGSIIQF", +"CINGLMLL", +"DGSIIQFP", +"LAAECTIF", +"SEYCRHGT", +"PVCINGLM", +"VLAAECTI", +"LEGSVAYE", +"LEGSVRVV", +"GSIIQFPN", +"VCINGLML", +"CSAYTVEL", +"VLEGSVAY", +"TPVCINGL", +"QFPNTYLE", +"FPNTYLEG", +"EGSVRVVT", +"NVLEGSVA", +"YLEGSVRV", +"YVLMDGSI", +"EGSVAYES", +"GSVRVVTT", +"TSEAVEAP", +"SRIKASMP", +"RYVLMDGS", +"GTPVCING", +"SVAYESLR", +"IIQFPNTY", +"IQFPNTYL", +"AYESLRPD", +"IQQKLALG", +"TYLEGSVR", +"GSVAYESL", +"VAYESLRP", +"SIIQFPNT", +"PNTYLEGS", +"SEAVEAPL", +"NTYLEGSV", +"VGTPVCIN", +"LVGTPVCI", +"AAECTIFK", +"AECTIFKD", +"ECTIFKDA", +"YCYDTNVL", +"PLVGTPVC", +"CYDTNVLE", +"PYCYDTNV", +"CTIFKDAS", +"DTNVLEGS", +"YDTNVLEG", +"TNVLEGSV", +"SVRVVTTF", +"EAVEAPLV", +"TIFKDASG", +"GLAAVNSV", +"AVEAPLVG", +"APLVGTPV", +"EAPLVGTP", +"FKDASGKP", +"LAAVNSVP", +"VEAPLVGT", +"THGLAAVN", +"IFKDASGK", +"AAVNSVPW", +"ATHGLAAV", +"HGLAAVNS", +"LRPDTRYV", +"YESLRPDT", +"RPDTRYVL", +"ESLRPDTR", +"SLRPDTRY", +"QQKLALGG", +"TRYVLMDG", +"DTRYVLMD", +"LATHGLAA", +"PDTRYVLM", +"TLATHGLA", +"KTLATHGL", +"LKTLATHG", +"RIKASMPT", +"IKASMPTT", +"VRVVTTFD", +"DSEYCRHG", +"TFDSEYCR", +"FDSEYCRH", +"KASMPTTI", +"TTFDSEYC", +"VTTFDSEY", +"VVTTFDSE", +"IGDELKIN", +"IIGDELKI", +"RVVTTFDS", +"YPIIGDEL", +"PIIGDELK", +"EYPIIGDE", +"QKLALGGS", +"AVHFISNS", +"ASMPTTIA", +"VISSDVLV", +"PVSEEVVE", +"SSDVLVNN", +"VVISSDVL", +"YFAVHFIS", +"GDELKINA", +"DELKINAA", +"ELKINAAC", +"NRVVISSD", +"LKINAACR", +"ISSDVLVN", +"KINAACRK", +"RVVISSDV", +"NNRVVISS", +"ENNRVVIS", +"FAVHFISN", +"IRENNRVV", +"RENNRVVI", +"IIRENNRV", +"LIIRENNR", +"INAACRKV", +"KLALGGSV", +"FVAAIFYL", +"VAAIFYLI", +"LFVAAIFY", +"RLIIRENN", +"NAACRKVQ", +"ANSTVLSF", +"PANSTVLS", +"IDVQQWGF", +"VPANSTVL", +"EVPANSTV", +"GRLIIREN", +"FELDERID", +"LALGGSVA", +"MIDVQQWG", +"KCSAYTVE", +"TFELDERI", +"DVQQWGFT", +"VQQWGFTG", +"GNLQSNHD", +"NLQSNHDL", +"LQSNHDLY", +"AHVASCDA", +"VHGNAHVA", +"QSNHDLYC", +"QQWGFTGN", +"NATEVPAN", +"QVHGNAHV", +"QWGFTGNL", +"VLSFCAFA", +"VAKSHNIA", +"LYCQVHGN", +"ELDERIDK", +"NHDLYCQV", +"YCQVHGNA", +"SNHDLYCQ", +"FTGNLQSN", +"LSFCAFAV", +"HGNAHVAS", +"NAHVASCD", +"ATEVPANS", +"LDERIDKV", +"CQVHGNAH", +"WGFTGNLQ", +"HDLYCQVH", +"HVASCDAI", +"SFCAFAVD", +"DLYCQVHG", +"VASCDAIM", +"GFTGNLQS", +"FMIDVQQW", +"TEVPANST", +"TGNLQSNH", +"SYFAVHFI", +"GNAHVASC", +"TVLSFCAF", +"STVLSFCA", +"PFMIDVQQ", +"NSTVLSFC", +"AACRKVQH", +"EKCSAYTV", +"DERIDKVL", +"NPFMIDVQ", +"QVAKSHNI", +"SIVCRFDT", +"AQVAKSHN", +"QTACTDDN", +"AKSHNIAL", +"YNPFMIDV", +"TQTACTDD", +"VYNPFMID", +"ACTDDNAL", +"CTDDNALA", +"TACTDDNA", +"KSHNIALI", +"INAQVAKS", +"ERIDKVLN", +"YVYNPFMI", +"WNVKDFMS", +"TTQTACTD", +"GTTQTACT", +"DYVYNPFM", +"TDDNALAY", +"NVKDFMSL", +"NSIVCRFD", +"HINAQVAK", +"NAQVAKSH", +"SHNIALIW", +"CERSEAGV", +"TCERSEAG", +"KDFMSLSE", +"CRFDTRVL", +"GNATEVPA", +"QAGNATEV", +"ACRKVQHM", +"ERSEAGVC", +"RHINAQVA", +"MQNCVLKL", +"IWNVKDFM", +"AGNATEVP", +"RFDTRVLS", +"VKDFMSLS", +"IVCRFDTR", +"LIWNVKDF", +"IALIWNVK", +"DFMSLSEQ", +"DKVLNEKC", +"ALIWNVKD", +"ARHINAQV", +"LQAGNATE", +"KGRLIIRE", +"RSEAGVCV", +"FCAFAVDA", +"FDTRVLSN", +"HNIALIWN", +"VCRFDTRV", +"IDKVLNEK", +"ACWHHSIG", +"TYACWHHS", +"NIALIWNV", +"SEAGVCVS", +"YACWHHSI", +"FDYVYNPF", +"KVLNEKCS", +"RLQAGNAT", +"DTYACWHH", +"SDTYACWH", +"RIDKVLNE", +"CSARHINA", +"VRLQAGNA", +"NEKCSAYT", +"DTRVLSNL", +"FMSLSEQL", +"CDRRATCF", +"LNEKCSAY", +"FSYFAVHF", +"CLFWNCNV", +"DNALAYYN", +"SARHINAQ", +"DRRATCFS", +"RRATCFST", +"NDPVGFTL", +"EAGVCVST", +"CWHHSIGF", +"VQHMVVKA", +"PKAIKCVP", +"MSLSEQLR", +"DDNALAYY", +"RKVQHMVV", +"KVQHMVVK", +"CRKVQHMV", +"VLNEKCSA", +"WHHSIGFD", +"SLSEQLRK", +"LSEQLRKQ", +"LFWNCNVD", +"DPVGFTLK", +"NALAYYNT", +"TRVLSNLN", +"CAFAVDAA", +"SEQLRKQI", +"SIGFDYVY", +"FLFVAAIF", +"KVTFGDDT", +"KAIKCVPQ", +"GFDYVYNP", +"AIKCVPQA", +"VLHDIGNP", +"EQLRKQIR", +"SCAAGTTQ", +"KIEELFYS", +"ATCFSTAS", +"ASDTYACW", +"VTFGDDTV", +"TCFSTASD", +"CAAGTTQT", +"YKIEELFY", +"TASDTYAC", +"IGFDYVYN", +"LHDIGNPK", +"HHSIGFDY", +"PVLHDIGN", +"HSIGFDYV", +"RSAAKKNN", +"AAGTTQTA", +"STASDTYA", +"KFPVLHDI", +"QHMVVKAA", +"NPKAIKCV", +"RATCFSTA", +"DIGNPKAI", +"AGTTQTAC", +"ANDPVGFT", +"TFGDDTVI", +"LRKQIRSA", +"IGNPKAIK", +"NCNVDRYP", +"AAKKNNLP", +"FPVLHDIG", +"IEELFYSY", +"HMVVKAAL", +"IRSAAKKN", +"HDIGNPKA", +"GNPKAIKC", +"QLRKQIRS", +"SAAKKNNL", +"KKNNLPFK", +"QIRSAAKK", +"AKKNNLPF", +"VCLFWNCN", +"WNCNVDRY", +"FSTASDTY", +"CANDPVGF", +"KIVNNWLK", +"RKQIRSAA", +"MVVKAALL", +"KCVPQADV", +"FGDDTVIE", +"CFSTASDT", +"FWNCNVDR", +"IKCVPQAD", +"DKFPVLHD", +"LCDRRATC", +"KQIRSAAK", +"RVLSNLNL", +"KAYKIEEL", +"CSDKAYKI", +"AYKIEELF", +"SDKAYKIE", +"IVNNWLKQ", +"FYDAQPCS", +"MSCAAGTT", +"DAQPCSDK", +"YDAQPCSD", +"PCSDKAYK", +"GKIVNNWL", +"DKAYKIEE", +"EELFYSYA", +"GTCERSEA", +"ELFYSYAT", +"GDDTVIEV", +"DCSARHIN", +"QPCSDKAY", +"NITFELDE", +"NNWLKQLI", +"VNNWLKQL", +"AQPCSDKA", +"LFYSYATH", +"ITFELDER", +"VNITFELD", +"LADKFPVL", +"CVPQADVE", +"FFSYFAVH", +"LLADKFPV", +"AFAVDAAK", +"KSVNITFE", +"KGKYVQIP", +"VPQADVEW", +"GVCLFWNC", +"NWLKQLIK", +"LKGKYVQI", +"CDLKGKYV", +"TVIEVQGY", +"DTVIEVQG", +"VIEVQGYK", +"DDTVIEVQ", +"TKVTFGDD", +"SVNITFEL", +"YKSVNITF", +"ADKFPVLH", +"FAVDAAKA", +"DLKGKYVQ", +"WLKQLIKV", +"DGVCLFWN", +"TCANDPVG", +"VVKAALLA", +"YVQIPTTC", +"TDGVCLFW", +"IPTTCAND", +"QIPTTCAN", +"LKQLIKVT", +"WKFYDAQP", +"NVVTTKIA", +"KFYDAQPC", +"FCDLKGKY", +"FTDGVCLF", +"KFTDGVCL", +"GYKSVNIT", +"TVRLQAGN", +"KGFCDLKG", +"GFCDLKGK", +"DKFTDGVC", +"NPKGFCDL", +"PKGFCDLK", +"AVDAAKAY", +"ATVRLQAG", +"VEWKFYDA", +"GGKIVNNW", +"VKAALLAD", +"VQIPTTCA", +"TNCVKMLC", +"GKYVQIPT", +"KAALLADK", +"NCVKMLCT", +"KMLCTHTG", +"VKMLCTHT", +"LCTHTGTG", +"KNNLPFKL", +"CVKMLCTH", +"VVTTKIAL", +"PTTCANDP", +"PTKVTFGD", +"EWKFYDAQ", +"VDAAKAYK", +"GQPITNCV", +"VVNVVTTK", +"PITNCVKM", +"QPITNCVK", +"DQESFGGA", +"AKAYKDYL", +"QESFGGAS", +"PNPKGFCD", +"ITNCVKML", +"GGASCCLY", +"HPNPKGFC", +"ADVEWKFY", +"CCLYCRCH", +"MLCTHTGT", +"KGGKIVNN", +"CTHTGTGQ", +"CLYCRCHI", +"NNLPFKLT", +"VNVVTTKI", +"KQLIKVTL", +"GQAITVTP", +"QMSCAAGT", +"AALLADKF", +"DVEWKFYD", +"THTGTGQA", +"FGGASCCL", +"GASCCLYC", +"NMDQESFG", +"TGTGQAIT", +"LYCRCHID", +"VFLFVAAI", +"YCRCHIDH", +"SFGGASCC", +"MDQESFGG", +"DAAKAYKD", +"ESFGGASC", +"VTTKIALK", +"GTGQAITV", +"ASCCLYCR", +"PQADVEWK", +"KYVQIPTT", +"ANMDQESF", +"AAKAYKDY", +"HTGTGQAI", +"QADVEWKF", +"ALLADKFP", +"AITVTPEA", +"IDHPNPKG", +"TTCANDPV", +"IEVQGYKS", +"TGQAITVT", +"SCCLYCRC", +"EANMDQES", +"QAITVTPE", +"TVTPEANM", +"DHPNPKGF", +"APTKVTFG", +"ITVTPEAN", +"HIDHPNPK", +"GGQPITNC", +"PEANMDQE", +"QVVNVVTT", +"SGGQPITN", +"RCHIDHPN", +"TPEANMDQ", +"CHIDHPNP", +"VTPEANMD", +"VQGYKSVN", +"QGYKSVNI", +"RQVVNVVT", +"TRQVVNVV", +"NLPFKLTC", +"EVQGYKSV", +"CRCHIDHP", +"QLIKVTLV", +"HSDKFTDG", +"THSDKFTD", +"LPFKLTCA", +"KAYKDYLA", +"AYKDYLAS", +"LKGGAPTK", +"ELEPPCRF", +"GAPTKVTF", +"GGAPTKVT", +"RQMSCAAG", +"LEPPCRFV", +"FKLTCATT", +"FYSYATHS", +"VLSNLNLP", +"PFKLTCAT", +"PCRFVTDT", +"HFLPRVFS", +"ATTRQVVN", +"EPPCRFVT", +"KGGAPTKV", +"TTRQVVNV", +"PPCRFVTD", +"ATHSDKFT", +"IKGLNNLN", +"CRFVTDTP", +"KLTCATTR", +"FIKGLNNL", +"LVFLFVAA", +"TCATTRQV", +"YKDYLASG", +"KDYLASGG", +"YFIKGLNN", +"YSYATHSD", +"LYFIKGLN", +"LRQMSCAA", +"TELEPPCR", +"YATHSDKF", +"TTKIALKG", +"LTCATTRQ", +"YLYFIKGL", +"QDLKWARF", +"DYLASGGQ", +"CATTRQVV", +"YTELEPPC", +"YLASGGQP", +"RFVTDTPK", +"SYATHSDK", +"VKLQNNEL", +"LHFLPRVF", +"FVTDTPKG", +"VKYLYFIK", +"SDKFTDGV", +"LQDLKWAR", +"KYLYFIKG", +"LKGGKIVN", +"LASGGQPI", +"KVKYLYFI", +"DADSKIVQ", +"TKIALKGG", +"GPKVKYLY", +"ASGGQPIT", +"KLQNNELS", +"ADSKIVQL", +"LQNNELSP", +"QNNELSPV", +"VDADSKIV", +"KIALKGGK", +"IALKGGKI", +"AATVRLQA", +"YASALWEI", +"KGLNNLNR", +"KGPKVKYL", +"ALKGGKIV", +"VVDADSKI", +"DSKIVQLS", +"QQVVDADS", +"VTDTPKGP", +"GLNNLNRG", +"SKIVQLSE", +"IVQLSEIS", +"QVVDADSK", +"IQQVVDAD", +"KIVQLSEI", +"VQLSEISM", +"MMVTNNTF", +"VLALLSDL", +"PKVKYLYF", +"GGRFVLAL", +"DLQDLKWA", +"EIQQVVDA", +"ASALWEIQ", +"TDTPKGPK", +"FVLALLSD", +"VLGSLAAT", +"QLSEISMD", +"TPKGPKVK", +"LLSDLQDL", +"LNRGMVLG", +"PKGPKVKY", +"DTPKGPKV", +"TFTYASAL", +"SALWEIQQ", +"SEISMDNS", +"LSEISMDN", +"LSDLQDLK", +"NSPNLAWP", +"NNELSPVA", +"ALWEIQQV", +"SDLQDLKW", +"FTYASALW", +"SPNLAWPL", +"GRFVLALL", +"NNLNRGMV", +"DLKWARFP", +"ALLSDLQD", +"EISMDNSP", +"WEIQQVVD", +"DNSPNLAW", +"ALRQMSCA", +"LWEIQQVV", +"LKWARFPK", +"LNNLNRGM", +"ALAYYNTT", +"FLHFLPRV", +"GSLAATVR", +"ARFPKSDG", +"LAATVRLQ", +"MVLGSLAA", +"NLNRGMVL", +"NELSPVAL", +"PKSDGTGT", +"MDNSPNLA", +"LGSLAATV", +"WARFPKSD", +"RFPKSDGT", +"SMDNSPNL", +"NRGMVLGS", +"VALRQMSC", +"FPKSDGTG", +"KWARFPKS", +"LSPVALRQ", +"AWPLIVTA", +"PVALRQMS", +"LKPVSEEV", +"KPVSEEVV", +"ISMDNSPN", +"NNTFTLKG", +"NMMVTNNT", +"NLAWPLIV", +"RFVLALLS", +"SLAATVRL", +"LAWPLIVT", +"ELSPVALR", +"TLVFLFVA", +"LIKVTLVF", +"TYASALWE", +"GMVLGSLA", +"PNLAWPLI", +"DLKPVSEE", +"RGMVLGSL", +"SPVALRQM", +"SAVKLQNN", +"KGGRFVLA", +"LFDMSKFP", +"AVKLQNNE", +"EIKDTEKY", +"VTNNTFTL", +"FTLKGGAP", +"VTLVFLFV", +"LALLSDLQ", +"KVTLVFLF", +"IKVTLVFL", +"TLKGGAPT", +"NSAVKLQN", +"TNNTFTLK", +"PNMMVTNN", +"DMSKFPLK", +"TFTLKGGA", +"MVTNNTFT", +"MSKFPLKL", +"LAPNMMVT", +"FDMSKFPL", +"ANSAVKLQ", +"APNMMVTN", +"LAYYNTTK", +"AYYNTTKG", +"KDTEKYCA", +"NTFTLKGG", +"RANSAVKL", +"LACEDLKP", +"EDLKPVSE", +"LRANSAVK", +"ALAPNMMV", +"YCALAPNM", +"ALRANSAV", +"CALAPNMM", +"CEDLKPVS", +"ACEDLKPV", +"VTALRANS", +"TEKYCALA", +"TALRANSA", +"DTEKYCAL", +"IKDTEKYC", +"WPLIVTAL", +"DFLHFLPR", +"CVLKLKVD", +"PLIVTALR", +"KYCALAPN", +"NCVLKLKV", +"EKYCALAP", +"YYNTTKGG", +"QNCVLKLK", +"LIVTALRA", +"VLKLKVDT", +"IVTALRAN", +"KLKVDTAN", +"SMPTTIAK", +"LKLKVDTA", +"YNTTKGGR", +"GDFLHFLP", +"TTFTYASA", +"GTTFTYAS", +"NTTKGGRF", +"TTKGGRFV", +"DGTTFTYA", +"TKGGRFVL", +"NGDFLHFL", +"LKVDTANP", +"TFTRLQSL", +"NTFTRLQS", +"NNTDFSRV", +"MQLFFSYF", +"TPNNTDFS", +"WNTFTRLQ", +"PNNTDFSR", +"DTPNNTDF", +"LWNTFTRL", +"YVDTPNNT", +"TGYVDTPN", +"GYVDTPNN", +"VDTPNNTD", +"VGFTLKNT", +"DGTGTIYT", +"IMQLFFSY", +"GTGTIYTE", +"TGTIYTEL", +"GTIYTELE", +"GFTLKNTV", +"PVGFTLKN", +"ILFTRFFY", +"IQITISSF", +"SDGTGTIY", +"TIQITISS", +"YILFTRFF", +"IYTELEPP", +"YNLWNTFT", +"TIYTELEP", +"IEYPIIGD", +"SLETIQIT", +"KSDGTGTI", +"LFTRFFYV", +"QITISSFK", +"LETIQITI", +"LFFSYFAV", +"ETIQITIS", +"NLWNTFTR", +"AYILFTRF", +"ITISSFKW", +"FTLKNTVC", +"LAYILFTR", +"ISSFKWDL", +"TISSFKWD", +"TLKNTVCT", +"FLAYILFT", +"PSLETIQI", +"EWFLAYIL", +"SSFKWDLT", +"CSVCLSGL", +"WFLAYILF", +"YPSLETIQ", +"DLTAFGLV", +"VCLSGLDS", +"SFKWDLTA", +"GYLNSTNV", +"RVDWTIEY", +"VDWTIEYP", +"SVCLSGLD", +"TAFGLVAE", +"LTAFGLVA", +"DWTIEYPI", +"TIEYPIIG", +"WTIEYPII", +"AFGLVAEW", +"CLSGLDSL", +"NVTIATYC", +"VAEWFLAY", +"FKWDLTAF", +"FGLVAEWF", +"AEWFLAYI", +"WDLTAFGL", +"FFYVLGLA", +"KWDLTAFG", +"FTRFFYVL", +"TRFFYVLG", +"RFFYVLGL", +"SLFDMSKF", +"YSLFDMSK", +"GLVAEWFL", +"SLLSKGRL", +"LSGLDSLD", +"LDTYPSLE", +"LVAEWFLA", +"SKGRLIIR", +"LLSKGRLI", +"TYPSLETI", +"TIATYCTG", +"SLDTYPSL", +"TVIWDYKR", +"GLDSLDTY", +"SYSLFDMS", +"LDSLDTYP", +"DSLDTYPS", +"LSKGRLII", +"LSLLSKGR", +"QLFFSYFA", +"IWDYKRDA", +"WDYKRDAP", +"SGLDSLDT", +"DYKRDAPA", +"VIWDYKRD", +"ILSLLSKG", +"VTIATYCT", +"DTYPSLET", +"YKRDAPAH", +"KRDAPAHI", +"REGYLNST", +"GVCVSTSG", +"SSYSLFDM", +"RDAPAHIS", +"LSSYSLFD", +"DMILSLLS", +"MILSLLSK", +"NDMILSLL", +"IFYLITPV", +"AGVCVSTS", +"EGYLNSTN", +"INDMILSL", +"KRVDWTIE", +"GKPVPYCY", +"YREGYLNS", +"KPVPYCYD", +"AIMQLFFS", +"VPYCYDTN", +"PVPYCYDT", +"FYVLGLAA", +"QLSSYSLF", +"VKRVDWTI", +"QINDMILS", +"DASGKPVP", +"KDASGKPV", +"TNGDFLHF", +"ASGKPVPY", +"SGKPVPYC", +"AAIMQLFF", +"DAIMTRCL", +"IQLSSYSL", +"ASCDAIMT", +"FVKRVDWT", +"FYLITPVH", +"AIMTRCLA", +"CFVKRVDW", +"LAAIMQLF", +"YVLGLAAI", +"VLGLAAIM", +"LGLAAIMQ", +"GLAAIMQL", +"SCDAIMTR", +"DLLLDDFV", +"YLITPVHV", +"CDAIMTRC", +"NTVIWDYK", +"SSEAFLIG", +"DDFVEIIK", +"IMTRCLAV", +"LLLDDFVE", +"ANTVIWDY", +"LDDFVEII", +"LLDDFVEI", +"MTRCLAVH", +"IDLLLDDF", +"SEAFLIGC", +"VIDLLLDD", +"EAFLIGCN", +"SSSEAFLI", +"AFLIGCNY", +"ECFVKRVD", +"FLIGCNYL", +"HANYIFWR", +"HECFVKRV", +"MHANYIFW", +"ANYIFWRN", +"VHECFVKR", +"YIFWRNTN", +"TRCLAVHE", +"DYYRSLPG", +"AVHECFVK", +"YYRSLPGV", +"TYNLWNTF", +"GQINDMIL", +"RCLAVHEC", +"NYIFWRNT", +"LIGCNYLG", +"CLAVHECF", +"LAVHECFV", +"NNDYYRSL", +"NDYYRSLP", +"YVMHANYI", +"GYVMHANY", +"LNNDYYRS", +"VLNNDYYR", +"DGYVMHAN", +"ASSSEAFL", +"VMHANYIF", +"VNASSSEA", +"DTYNLWNT", +"NASSSEAF", +"AANTVIWD", +"SVIDLLLD", +"VCVSTSGR", +"IGCNYLGK", +"AAIFYLIT", +"WVLNNDYY", +"IDGYVMHA", +"YLNSTNVT", +"CVSTSGRW", +"FDTYNLWN", +"RWVLNNDY", +"LNSTNVTI", +"IATYCTGS", +"VSTSGRWV", +"STSGRWVL", +"AIFYLITP", +"TSGRWVLN", +"GRWVLNND", +"IAANTVIW", +"ATYCTGSI", +"AVMSLKEG", +"SGRWVLNN", +"NSTNVTIA", +"DIAANTVI", +"STNVTIAT", +"VAFNVVNK", +"MPTTIAKN", +"QIDGYVMH", +"AFNVVNKG", +"VVNKGHFD", +"EGQINDMI", +"TNVTIATY", +"FNVVNKGH", +"VNKGHFDG", +"KEGQINDM", +"VMSLKEGQ", +"NVVNKGHF", +"VKILNNLG", +"PEVKILNN", +"VDIAANTV", +"PVNVAFEL", +"GVDIAANT", +"VNVAFELW", +"NVAFELWA", +"EVKILNNL", +"LPVNVAFE", +"FVEIIKSQ", +"DFVEIIKS", +"TIDYTEIS", +"ILNNLGVD", +"KILNNLGV", +"PIQLSSYS", +"LKEGQIND", +"LNNLGVDI", +"EQIDGYVM", +"TLPVNVAF", +"VELFENKT", +"ELFENKTT", +"IFWRNTNP", +"VEIIKSQD", +"EIIKSQDL", +"IIKSQDLS", +"PVPEVKIL", +"VPEVKILN", +"GCNYLGKP", +"TTLPVNVA", +"KPVPEVKI", +"NNLGVDIA", +"KTTLPVNV", +"FENKTTLP", +"NKTTLPVN", +"NLGVDIAA", +"IKSQDLSV", +"IKPVPEVK", +"GHFDGQQG", +"DVELFENK", +"LGVDIAAN", +"VTIDYTEI", +"ENKTTLPV", +"LFENKTTL", +"SLKEGQIN", +"MSLKEGQI", +"NKGHFDGQ", +"KVTIDYTE", +"VKVTIDYT", +"VDVELFEN", +"KSQDLSVV", +"NIKPVPEV", +"VAFELWAK", +"SQDLSVVS", +"NPIQLSSY", +"VVKVTIDY", +"DGVDVELF", +"SKVVKVTI", +"TAVMSLKE", +"GVDVELFE", +"RNIKPVPE", +"AFELWAKR", +"KGHFDGQQ", +"FELWAKRN", +"KVVKVTID", +"ELWAKRNI", +"KRNIKPVP", +"LWAKRNIK", +"AKRNIKPV", +"VDGVDVEL", +"FWRNTNPI", +"WAKRNIKP", +"KVDGVDVE", +"TNPIQLSS", +"TKVDGVDV", +"WWTAFVTN", +"VYTKVDGV", +"VSKVVKVT", +"TVYTKVDG", +"NTVYTKVD", +"WTAFVTNV", +"AIKITEHS", +"SIINNTVY", +"NNTVYTKV", +"IINNTVYT", +"RNTNPIQL", +"INNTVYTK", +"DLSVVSKV", +"QDLSVVSK", +"VAIKITEH", +"YTKVDGVD", +"NVNASSSE", +"WRNTNPIQ", +"TNVNASSS", +"NTNPIQLS", +"VSIINNTV", +"ITEHSWNA", +"KITEHSWN", +"LKNTVCTV", +"SVVSKVVK", +"VTNVNASS", +"IKITEHSW", +"GTAVMSLK", +"VVSKVVKV", +"LSVVSKVV", +"HFDGQQGE", +"PVSIINNT", +"TAFVTNVN", +"KFPLKLRG", +"GHFAWWTA", +"MGHFAWWT", +"AFVTNVNA", +"FVTNVNAS", +"TEHSWNAD", +"LMGHFAWW", +"FAWWTAFV", +"DGQQGEVP", +"HFAWWTAF", +"AWWTAFVT", +"SKFPLKLR", +"FDGQQGEV", +"HSWNADLY", +"GQQGEVPV", +"EHSWNADL", +"VPVSIINN", +"NADLYKLM", +"WNADLYKL", +"QFDTYNLW", +"EVPVSIIN", +"SWNADLYK", +"GEVPVSII", +"QQGEVPVS", +"ADLYKLMG", +"KNTVCTVC", +"LYKLMGHF", +"QGEVPVSI", +"DLYKLMGH", +"YKLMGHFA", +"NTVCTVCG", +"KLMGHFAW", +"TTNGDFLH", +"ANSIVCRF", +"PTTIAKNT", +"PANSIVCR", +"YPANSIVC", +"VDAVNLLT", +"TVCTVCGM", +"KQFDTYNL", +"GVDAVNLL", +"CGVDAVNL", +"LPGVFCGV", +"KLRGTAVM", +"FCGVDAVN", +"VFCGVDAV", +"PGVFCGVD", +"LRGTAVMS", +"GVFCGVDA", +"RYPANSIV", +"RGTAVMSL", +"FPLKLRGT", +"LKLRGTAV", +"DRYPANSI", +"CNVDRYPA", +"NVDRYPAN", +"VDRYPANS", +"PLKLRGTA", +"SLPGVFCG", +"TTIAKNTV", +"VCTVCGMW", +"RTTNGDFL", +"RSLPGVFC", +"YRSLPGVF", +"ALGGSVAI", +"LLLSVCLG", +"GYREGYLN", +"LSNLNLPG", +"FLLLSVCL", +"IWFLLLSV", +"LRTTNGDF", +"LLSVCLGS", +"TIAKNTVK", +"SNLNLPGC", +"TGYREGYL", +"LGGSVAIK", +"WFLLLSVC", +"NLNLPGCD", +"ILRTTNGD", +"NYLKSPNF", +"FNYLKSPN", +"YCTGYREG", +"SFNYLKSP", +"PCSVCLSG", +"CTGYREGY", +"LITPVHVM", +"ASFNYLKS", +"DTCFANKH", +"FANKHADF", +"LEASFNYL", +"IPCSVCLS", +"CFANKHAD", +"TCFANKHA", +"IAKNTVKS", +"EASFNYLK", +"NFSKLINI", +"ANKHADFD", +"TDTCFANK", +"LKSPNFSK", +"PNFSKLIN", +"YLKSPNFS", +"NKHADFDT", +"KHADFDTW", +"KSPNFSKL", +"SIPCSVCL", +"SPNFSKLI", +"DAPAHIST", +"STDTCFAN", +"AVITREVG", +"GSIPCSVC", +"AKNTVKSV", +"NTVKSVGK", +"VITREVGF", +"CLGSLIYS", +"CLEASFNY", +"ITREVGFV", +"VCLGSLIY", +"SVCLGSLI", +"KNTVKSVG", +"ASTDTCFA", +"FCLEASFN", +"APAHISTI", +"SYCTGYRE", +"PAHISTIG", +"PLIAAVIT", +"KFCLEASF", +"VKSVGKFC", +"TVKSVGKF", +"LGSLIYST", +"KSVGKFCL", +"IASTDTCF", +"LIAAVITR", +"LSVCLGSL", +"RDIASTDT", +"DIASTDTC", +"ACPLIAAV", +"AAVITREV", +"CPLIAAVI", +"AYVIYTSG", +"TRDIASTD", +"SVGKFCLE", +"IAAVITRE", +"GKFCLEAS", +"GSLIYSTA", +"NDKACPLI", +"VGKFCLEA", +"VTRDIAST", +"KACPLIAA", +"SLIYSTAA", +"PSYCTGYR", +"TGSIPCSV", +"DKACPLIA", +"TREVGFVV", +"GVTRDIAS", +"CTGSIPCS", +"GGVTRDIA", +"REVGFVVP", +"EVGFVVPG", +"DGGVTRDI", +"YCTGSIPC", +"ITPVHVMS", +"IDGGVTRD", +"VGFVVPGL", +"GFVVPGLP", +"GGSVAIKI", +"CSVIDLLL", +"VCSVIDLL", +"AIDGGVTR", +"AHISTIGV", +"CVCSVIDL", +"FVVPGLPG", +"GSVAIKIT", +"KCVCSVID", +"CTVCGMWK", +"LNLPGCDG", +"TILRTTNG", +"YKAIDGGV", +"SVAIKITE", +"KAIDGGVT", +"TYCTGSIP", +"SKCVCSVI", +"HISTIGVC", +"GTILRTTN", +"VVPGLPGT", +"GLPGTILR", +"TVCGMWKG", +"PGLPGTIL", +"LPGTILRT", +"PGTILRTT", +"VPGLPGTI", +"TDFSSEII", +"GYKAIDGG", +"DFSSEIIG", +"IGYKAIDG", +"HTDFSSEI", +"ISTIGVCS", +"IIGYKAID", +"LIYSTAAL", +"IYSTAALG", +"FSSEIIGY", +"EIIGYKAI", +"SEIIGYKA", +"SSEIIGYK", +"KHTDFSSE", +"YSTAALGV", +"ALGVLMSN", +"SKHTDFSS", +"STAALGVL", +"TAALGVLM", +"AALGVLMS", +"LGVLMSNL", +"GVLMSNLG", +"VCGMWKGY", +"CGMWKGYG", +"NLPGCDGG", +"VMSKHTDF", +"HVMSKHTD", +"VHVMSKHT", +"MSKHTDFS", +"TPVHVMSK", +"PVHVMSKH", +"VIYTSGST", +"STIGVCSM", +"WKGYGCSC", +"MWKGYGCS", +"KGYGCSCD", +"GMWKGYGC", +"YVIYTSGS", +"PMLQSADA", +"GYGCSCDQ", +"YGCSCDQL", +"GCSCDQLR", +"MLQSADAQ", +"CSCDQLRE", +"SCDQLREP", +"EPMLQSAD", +"TIGVCSMT", +"DQLREPML", +"CDQLREPM", +"LQSADAQS", +"REPMLQSA", +"QLREPMLQ", +"LREPMLQS", +"QSADAQSF", +"SADAQSFL", +"ADAQSFLN", +"YKLDGVVC", +"EQFKKGVQ", +"YEQFKKGV", +"VTYKLDGV", +"TYKLDGVV", +"PVTYKLDG", +"SYEQFKKG", +"KPVTYKLD", +"QFKKGVQI", +"VLMSNLGM", +"GMPSYCTG", +"FKKGAKLL", +"KKGAKLLH", +"KGAKLLHK", +"GAKLLHKP", +"AKLLHKPI", +"KLLHKPIV", +"LHKPIVWH", +"HKPIVWHV", +"LLHKPIVW", +"MPSYCTGY", +"LMSNLGMP", +"SNLGMPSY", +"MSNLGMPS", +"LGMPSYCT", +"NLGMPSYC", +"RIELGEIE", +"TIKPVTYK", +"IKPVTYKL", +"TTIKPVTY", +"TLSYEQFK", +"LSYEQFKK", +"YTTTIKPV", +"FKKGVQIP", +"KKGVQIPC", +"GTLSYEQF", +"TTTIKPVT", +"SYTTTIKP", +"KGVQIPCT", +"MGTLSYEQ", +"YMGTLSYE", +"GVQIPCTC", +"VQIPCTCG", +"ESPFVMMS", +"FVMMSAPP", +"HADFDTWF", +"VMMSAPPA", +"NSYTTTIK", +"KPIVWHVN", +"QESPFVMM", +"VQQESPFV", +"SPFVMMSA", +"MSAPPAQY", +"SAPPAQYE", +"MMSAPPAQ", +"QQESPFVM", +"LVQQESPF", +"YLVQQESP", +"APPAQYEL", +"ADFDTWFS", +"PFVMMSAP", +"PIVWHVNN", +"FSQRGGSY", +"IGVCSMTD", +"SQRGGSYT", +"YELKHGTF", +"PPAQYELK", +"ENSYTTTI", +"FDTWFSQR", +"DFDTWFSQ", +"PAQYELKH", +"IVWHVNNA", +"QYELKHGT", +"DTWFSQRG", +"WFSQRGGS", +"QIPCTCGK", +"AQYELKHG", +"VWHVNNAT", +"KENSYTTT", +"ELKHGTFT", +"YKHITSKE", +"QCGHYKHI", +"VFYKENSY", +"LKHGTFTC", +"HITSKETL", +"KHGTFTCA", +"TWFSQRGG", +"KHITSKET", +"ITSKETLY", +"QRGGSYTN", +"PCTCGKQA", +"GNYQCGHY", +"RGGSYTND", +"NYQCGHYK", +"HYKHITSK", +"HGTFTCAS", +"YQCGHYKH", +"TFTCASEY", +"CGHYKHIT", +"GTFTCASE", +"IPCTCGKQ", +"YKENSYTT", +"GHYKHITS", +"TKSSEYKG", +"SKETLYCI", +"CTCGKQAT", +"GSYTNDKA", +"WSTKPVET", +"LLTKSSEY", +"TSKETLYC", +"FYKENSYT", +"LWSTKPVE", +"CLWSTKPV", +"LTKSSEYK", +"GGSYTNDK", +"RCLWSTKP", +"WHVNNATN", +"SYTNDKAC", +"DVFYKENS", +"TDVFYKEN", +"ITDVFYKE", +"IRCLWSTK", +"KSSEYKGP", +"KETLYCID", +"ALLTKSSE", +"TNDKACPL", +"CIRCLWST", +"YTNDKACP", +"SFKKGAKL", +"SSEYKGPI", +"ETLYCIDG", +"YTGNYQCG", +"TGNYQCGH", +"SEYKGPIT", +"ASEYTGNY", +"FTCASEYT", +"GALLTKSS", +"EYKGPITD", +"CASEYTGN", +"EYTGNYQC", +"TLYCIDGA", +"PITDVFYK", +"KGPITDVF", +"GPITDVFY", +"SEYTGNYQ", +"VCSMTDIA", +"LYCIDGAL", +"VYRAFDIY", +"YKGPITDV", +"TCASEYTG", +"HVNNATNK", +"GVCSMTDI", +"YKQFDTYN", +"IDGALLTK", +"YCIDGALL", +"TVFFDGRV", +"LTVFFDGR", +"DGALLTKS", +"VVYRAFDI", +"MYMGTLSY", +"VYKQFDTY", +"CIDGALLT", +"CSMTDIAK", +"PLTVFFDG", +"DVVYRAFD", +"KLDGVVCT", +"SMTDIAKK", +"TDVVYRAF", +"MTDIAKKP", +"VFFDGRVD", +"VNNATNKA", +"STDVVYRA", +"FFDGRVDG", +"YANSVFNI", +"LDGVVCTE", +"TDIAKKPT", +"TSTDVVYR", +"WCIRCLWS", +"KYLVQQES", +"INIIIWFL", +"NIIIWFLL", +"TKYLVQQE", +"QATKYLVQ", +"ATKYLVQQ", +"IIIWFLLL", +"HYTPSFKK", +"YTPSFKKG", +"PSFKKGAK", +"TPSFKKGA", +"DGVVCTEI", +"GTSTDVVY", +"ANSVFNIC", +"NNATNKAT", +"NSVFNICQ", +"SVFNICQA", +"TGTSTDVV", +"KHYTPSFK", +"IIWFLLLS", +"GVVCTEID", +"NKATYKPN", +"CLYRNRDV", +"YRNRDVDT", +"LYRNRDVD", +"TNKATYKP", +"YECLYRNR", +"ECLYRNRD", +"APLTVFFD", +"LYECLYRN", +"TWCIRCLW", +"DIAKKPTE", +"ATNKATYK", +"NATNKATY", +"GKQATKYL", +"KQATKYLV", +"VFNICQAV", +"CGKQATKY", +"FDGRVDGQ", +"YKHYTPSF", +"LINIIIWF", +"TCGKQATK", +"RLYECLYR", +"FNICQAVT", +"HRLYECLY", +"NICQAVTA", +"KLINIIIW", +"TAYANSVF", +"AYANSVFN", +"ICQAVTAN", +"QHRLYECL", +"CQAVTANV", +"TTAYANSV", +"ATTAYANS", +"DGRVDGQV", +"KPNTWCIR", +"PNTWCIRC", +"NTWCIRCL", +"VVCTEIDP", +"YKPNTWCI", +"IAKKPTET", +"QAVTANVN", +"AVTANVNA", +"DATTAYAN", +"AKKPTETI", +"VTANVNAL", +"TYKPNTWC", +"KKPTETIC", +"KATYKPNT", +"ATYKPNTW", +"TANVNALL", +"ANVNALLS", +"ALLSTDGN", +"NALLSTDG", +"NVNALLST", +"VNALLSTD", +"LLSTDGNK", +"RVDGQVDL", +"GRVDGQVD", +"GDATTAYA", +"GQVDLFRN", +"LSTDGNKI", +"QVDLFRNA", +"FSKLINII", +"DGQVDLFR", +"VDGQVDLF", +"STDGNKIA", +"DLFRNARN", +"CAPLTVFF", +"SKLINIII", +"FRNARNGV", +"WVYKQFDT", +"RNARNGVL", +"LFRNARNG", +"LQHRLYEC", +"KPTETICA", +"ETICAPLT", +"VEAHGTGT", +"TICAPLTV", +"GTGTSTDV", +"AARLTPCG", +"ARNGVLIT", +"NARNGVLI", +"VDLFRNAR", +"ICAPLTVF", +"TDGNKIAD", +"NLQHRLYE", +"LTPCGTGT", +"CGTGTSTD", +"DAQSFLNR", +"RNLQHRLY", +"SGDATTAY", +"PTETICAP", +"TETICAPL", +"TPCGTGTS", +"VCTEIDPK", +"RLTPCGTG", +"VRNLQHRL", +"DGNKIADK", +"ARLTPCGT", +"GNKIADKY", +"SSGDATTA", +"PCGTGTST", +"DYKHYTPS", +"YVRNLQHR", +"FLNRVCGV", +"AQSFLNRV", +"TSSGDATT", +"GTSSGDAT", +"SFLNRVCG", +"GGTSSGDA", +"QSFLNRVC", +"KPGGTSSG", +"PGGTSSGD", +"LNRVCGVS", +"NKIADKYV", +"KYVRNLQH", +"KIADKYVR", +"DKYVRNLQ", +"ADKYVRNL", +"LWVYKQFD", +"IADKYVRN", +"QQIELKFN", +"VKPGGTSS", +"YVKPGGTS", +"CGQQQTTL", +"NRVCGVSA", +"LKFNPPAL", +"PKCDRAMP", +"ECAQVLSE", +"RVLNVVCK", +"KCDRAMPN", +"TCGQQQTT", +"NECAQVLS", +"GQQQTTLK", +"KFNPPALQ", +"GVLITEGS", +"NGVLITEG", +"VLITEGSV", +"ELKFNPPA", +"LSEMVMCG", +"LQPSVGPK", +"FYRLANEC", +"LYVKPGGT", +"QQQTTLKG", +"VLNVVCKT", +"QPSVGPKQ", +"RNGVLITE", +"ANECAQVL", +"VLSEMVMC", +"YRLANECA", +"RVCGVSAA", +"RLANECAQ", +"KTCGQQQT", +"LANECAQV", +"IELKFNPP", +"VCGVSAAR", +"AQVLSEMV", +"KRVLNVVC", +"NVVCKTCG", +"QVLSEMVM", +"RFYRLANE", +"CKTCGQQQ", +"CAQVLSEM", +"VVCKTCGQ", +"LNVVCKTC", +"CGVSAARL", +"SLYVKPGG", +"GVSAARLT", +"PSVGPKQA", +"QIELKFNP", +"VCKTCGQQ", +"LDSCKRVL", +"DSCKRVLN", +"KTQFNYYK", +"SCKRVLNV", +"NLDSCKRV", +"CKRVLNVV", +"CDRAMPNM", +"LIGEAVKT", +"QQTTLKGV", +"QTTLKGVE", +"VKTQFNYY", +"IGEAVKTQ", +"NMLRIMAS", +"LRIMASLV", +"RAMPNMLR", +"AMPNMLRI", +"TTLKGVEA", +"LITEGSVK", +"PNMLRIMA", +"DRAMPNML", +"ANLDSCKR", +"TLIGEAVK", +"MPNMLRIM", +"GEAVKTQF", +"LSHRFYRL", +"TQSRNLQE", +"MLRIMASL", +"RIMASLVL", +"GLQPSVGP", +"TLKGVEAV", +"SLVLARKH", +"LKGVEAVM", +"HRFYRLAN", +"SVGPKQAS", +"EAVKTQFN", +"SHRFYRLA", +"HANLDSCK", +"VGPKQASL", +"CSLSHRFY", +"QHANLDSC", +"VMYMGTLS", +"IMASLVLA", +"LFQHANLD", +"NGVTLIGE", +"ASLVLARK", +"SLSHRFYR", +"FQHANLDS", +"TQFNYYKK", +"LVLARKHT", +"ARKHTTCC", +"KGLQPSVG", +"RKHTTCCS", +"MASLVLAR", +"CCSLSHRF", +"KHTTCCSL", +"QFNYYKKV", +"GPKQASLN", +"SSKCVCSV", +"FNYYKKVD", +"NPPALQDA", +"HTTCCSLS", +"NYYKKVDG", +"VKGLQPSV", +"VLARKHTT", +"TMSYLFQH", +"FNPPALQD", +"LNGVTLIG", +"LARKHTTC", +"VSAARLTP", +"ETMSYLFQ", +"RETMSYLF", +"YLFQHANL", +"AVKTQFNY", +"TCCSLSHR", +"VTLIGEAV", +"AVMYMGTL", +"TTCCSLSH", +"MSYLFQHA", +"SYLFQHAN", +"SAARLTPC", +"PKQASLNG", +"ITEGSVKG", +"KGVEAVMY", +"SLNGVTLI", +"GVTLIGEA", +"ASLNGVTL", +"SVKGLQPS", +"QASLNGVT", +"EAVMYMGT", +"TEGSVKGL", +"VEAVMYMG", +"EGSVKGLQ", +"AYYRARAG", +"YRARAGEA", +"GSVKGLQP", +"YYRARAGE", +"GVEAVMYM", +"KQASLNGV", +"YFDCYDGG", +"QDAYYRAR", +"LQDAYYRA", +"YYKKVDGV", +"DAYYRARA", +"ALQDAYYR", +"PALQDAYY", +"PPALQDAY", +"LFVVEVVD", +"GSSKCVCS", +"NLPTMCDI", +"YNLPTMCD", +"DYDYYRYN", +"TMCDIRQL", +"LLFVVEVV", +"CDIRQLLF", +"LPTMCDIR", +"MCDIRQLL", +"DIRQLLFV", +"YYRYNLPT", +"PTMCDIRQ", +"KRTTCFSV", +"YDYYRYNL", +"DKRTTCFS", +"SDYDYYRY", +"LDKRTTCF", +"NVAFQTVK", +"LLDKRTTC", +"QLLFVVEV", +"DYYRYNLP", +"IRQLLFVV", +"RQLLFVVE", +"LLLDKRTT", +"FVVEVVDK", +"YRYNLPTM", +"RYNLPTMC", +"KYFDCYDG", +"NNVAFQTV", +"DKYFDCYD", +"TNNVAFQT", +"QTVKPGNF", +"TVKPGNFN", +"LTNNVAFQ", +"VDKYFDCY", +"FQTVKPGN", +"ALTNNVAF", +"AALTNNVA", +"VAFQTVKP", +"TSGSTGRP", +"VEVVDKYF", +"RTTCFSVA", +"VKPGNFNK", +"PGNFNKDF", +"NFCALILA", +"KPGNFNKD", +"AFQTVKPG", +"VVEVVDKY", +"VAALTNNV", +"TTCFSVAA", +"SVAALTNN", +"LKHFFFAQ", +"FSVAALTN", +"CFSVAALT", +"EVVDKYFD", +"VVDKYFDC", +"SGSTGRPK", +"FFAQDGNA", +"YKKVDGVV", +"FFFAQDGN", +"HFFFAQDG", +"CALILAYC", +"FCALILAY", +"LILAYCNK", +"ALILAYCN", +"KHFFFAQD", +"ISDYDYYR", +"FAQDGNAA", +"TCFSVAAL", +"AISDYDYY", +"AAISDYDY", +"FNKDFYDF", +"NAAISDYD", +"AQDGNAAI", +"NKDFYDFA", +"GNAAISDY", +"KDFYDFAV", +"DFYDFAVS", +"QDGNAAIS", +"DGNAAISD", +"FYDFAVSK", +"QLPETYFT", +"GFFKEGSS", +"YDFAVSKG", +"FFKEGSSV", +"NFNKDFYD", +"DFAVSKGF", +"FAVSKGFF", +"SKGFFKEG", +"KGFFKEGS", +"VSKGFFKE", +"KEGSSVEL", +"GSSVELKH", +"EGSSVELK", +"SSVELKHF", +"FKEGSSVE", +"VELKHFFF", +"SVELKHFF", +"AVSKGFFK", +"ELKHFFFA", +"KLAKKFDT", +"GNFNKDFY", +"KKVDGVVQ", +"KVDGVVQQ", +"LAKKFDTF", +"KFDTFNGE", +"FDTFNGEC", +"PETYFTQS", +"DTFNGECP", +"ETYFTQSR", +"KKFDTFNG", +"AKKFDTFN", +"TFNGECPN", +"VDGVVQQL", +"DGVVQQLP", +"QSRNLQEF", +"VVQQLPET", +"LPETYFTQ", +"GVVQQLPE", +"SLWVYKQF", +"IDYKHYTP", +"LPGCDGGS", +"AIDYKHYT", +"GCINANQV", +"AKKFDIFN", +"KLAKKFDI", +"LAKKFDIF", +"KKFDIFNG", +"FDIFNGEC", +"KFDIFNGE", +"CTEIDPKL", +"FDCYDGGC", +"DIFNGECP", +"IFNGECPN", +"DCYDGGCI", +"QQLPETYF", +"GSTGRPKG", +"VQQLPETY", +"CYDGGCIN", +"YDGGCINA", +"TGSSKCVC", +"VAIDYKHY", +"TEIDPKLD", +"TYFTQSRN", +"DGGCINAN", +"YLGKPREQ", +"FTQSRNLQ", +"CNYLGKPR", +"NYLGKPRE", +"LGKPREQI", +"FSLWVYKQ", +"REQIDGYV", +"GKPREQID", +"KPREQIDG", +"PREQIDGY", +"WDYPKCDR", +"GWDYPKCD", +"GGCINANQ", +"DYPKCDRA", +"MGWDYPKC", +"LMGWDYPK", +"YFTQSRNL", +"TSKFYGGW", +"YPKCDRAM", +"SKFYGGWH", +"KFYGGWHN", +"VVIGTSKF", +"TVVIGTSK", +"HLMGWDYP", +"PHLMGWDY", +"NPHLMGWD", +"VVAIDYKH", +"YGGWHNML", +"FYGGWHNM", +"ELKVTFFP", +"RMYRTGDL", +"ENPHLMGW", +"KKPASREL", +"VENPHLMG", +"ASRELKVT", +"GGWHNMLK", +"SRELKVTF", +"PFNKWGKA", +"FPFNKWGK", +"PASRELKV", +"RELKVTFF", +"GWHNMLKT", +"KPASRELK", +"GFSLWVYK", +"YSDVENPH", +"SDVENPHL", +"DVVAIDYK", +"EIDPKLDN", +"DVENPHLM", +"VYSDVENP", +"YKKPASRE", +"LKTVYSDV", +"GFPFNKWG", +"ARLYYDSM", +"KTVYSDVE", +"AGFPFNKW", +"GDVVAIDY", +"FNKWGKAR", +"TVYSDVEN", +"VIGTSKFY", +"NKWGKARL", +"WHNMLKTV", +"FKFVCDNI", +"KFVCDNIK", +"SAGFPFNK", +"VCDNIKFA", +"MLKTVYSD", +"KARLYYDS", +"NMLKTVYS", +"HNMLKTVY", +"KWGKARLY", +"DNIKFADD", +"FVCDNIKF", +"GTSKFYGG", +"CDNIKFAD", +"GKARLYYD", +"IGTSKFYG", +"NIKFADDL", +"WGKARLYY", +"IKFADDLN", +"KKDNSYFT", +"IDPKLDNY", +"FADDLNQL", +"KSAGFPFN", +"YYKKDNSY", +"ADDLNQLT", +"TFFPDLNG", +"YKKDNSYF", +"LKVTFFPD", +"PFELEDFI", +"FELEDFIP", +"DPKLDNYY", +"DNYYKKDN", +"KFADDLNQ", +"VTFFPDLN", +"NYYKKDNS", +"NFKFVCDN", +"SPFELEDF", +"KDNSYFTE", +"KVTFFPDL", +"LDNYYKKD", +"ESPFELED", +"DKSAGFPF", +"PKLDNYYK", +"LDKSAGFP", +"KLDNYYKK", +"LLIGLAKR", +"GYKKPASR", +"RLYYDSMS", +"ELEDFIPM", +"NLDKSAGF", +"KESPFELE", +"FKESPFEL", +"LIGLAKRF", +"LYYDSMSY", +"LEDFIPMD", +"SRNLQEFK", +"DNSYFTEQ", +"YYDSMSYE", +"NGDVVAID", +"FPDLNGDV", +"FFPDLNGD", +"LTGYKKPA", +"NNLDKSAG", +"YDSMSYED", +"IGLAKRFK", +"DNFKFVCD", +"TGYKKPAS", +"QLTGYKKP", +"PDLNGDVV", +"LNQLTGYK", +"GLAKRFKE", +"NQLTGYKK", +"DSMSYEDQ", +"INANQVIV", +"DFIPMDST", +"VNNLDKSA", +"EDFIPMDS", +"LAKRFKES", +"FDNFKFVC", +"SFDNFKFV", +"DLNQLTGY", +"DDLNQLTG", +"SMSYEDQD", +"DLNGDVVA", +"TVKNYFIT", +"PMDSTVKN", +"IVNNLDKS", +"MDSTVKNY", +"AKRFKESP", +"TEQPIDLV", +"NSYFTEQP", +"ANQVIVNN", +"LNGDVVAI", +"DSTVKNYF", +"RFKESPFE", +"MSYEDQDA", +"KRFKESPF", +"CINANQVI", +"NANQVIVN", +"EQPIDLVP", +"RNLQEFKP", +"FIPMDSTV", +"NQVIVNNL", +"STVKNYFI", +"SYEDQDAL", +"QVIVNNLD", +"FTEQPIDL", +"YFTEQPID", +"VIVNNLDK", +"QPIDLVPN", +"SYFTEQPI", +"IPMDSTVK", +"VIPTITQM", +"PIDLVPNQ", +"NLQEFKPR", +"RTVAGVSI", +"YEDQDALF", +"PTITQMNL", +"QMNLKYAI", +"RNVIPTIT", +"TQMNLKYA", +"NVIPTITQ", +"AGVSICST", +"TKRNVIPT", +"MNLKYAIS", +"NLKYAISA", +"VAGVSICS", +"VKNYFITD", +"TVAGVSIC", +"KYAISAKN", +"IPTITQMN", +"DQDALFAY", +"AISAKNRA", +"QDALFAYT", +"TITQMNLK", +"PGCDGGSL", +"LKYAISAK", +"EDQDALFA", +"YAISAKNR", +"LQEFKPRS", +"KRNVIPTI", +"FAYTKRNV", +"KNYFITDA", +"LFAYTKRN", +"NASFDNFK", +"YTKRNVIP", +"DALFAYTK", +"ARTVAGVS", +"ITQMNLKY", +"ISAKNRAR", +"ASFDNFKF", +"RARTVAGV", +"ALFAYTKR", +"NRARTVAG", +"AYTKRNVI", +"KNRARTVA", +"AKNRARTV", +"GVSICSTM", +"SAKNRART", +"VSICSTMT", +"QEFKPRSQ", +"SICSTMTN", +"RQFHQKLL", +"NYFITDAQ", +"NRQFHQKL", +"ICSTMTNR", +"QTGSSKCV", +"TNRQFHQK", +"ATVVIGTS", +"EFKPRSQM", +"MTNRQFHQ", +"LGGLHLLI", +"GDVRETMS", +"GLHLLIGL", +"LGDVRETM", +"GATVVIGT", +"GGLHLLIG", +"CSTMTNRQ", +"YFITDAQT", +"DVRETMSY", +"QLGGLHLL", +"STMTNRQF", +"VRETMSYL", +"YGDFSHSQ", +"FITDAQTG", +"HSQLGGLH", +"TMTNRQFH", +"LHLLIGLA", +"ELGDVRET", +"SQLGGLHL", +"ITDAQTGS", +"SHSQLGGL", +"TDAQTGSS", +"HLLIGLAK", +"DAQTGSSK", +"FSHSQLGG", +"RGATVVIG", +"DFSHSQLG", +"GDFSHSQL", +"PNASFDNF", +"GELGDVRE", +"AQTGSSKC", +"VGELGDVR", +"TVGELGDV", +"DLVPNQPY", +"KTVGELGD", +"VYGDFSHS", +"TRGATVVI", +"IDLVPNQP", +"IVYGDFSH", +"STGRPKGV", +"NKTVGELG", +"QMEIDFLE", +"HIVYGDFS", +"ILAYCNKT", +"AYCNKTVG", +"CNKTVGEL", +"LAYCNKTV", +"YCNKTVGE", +"RYKLEGYA", +"MEIDFLEL", +"ERYKLEGY", +"IERYKLEG", +"LVPNQPYP", +"FKPRSQME", +"EHIVYGDF", +"EIDFLELA", +"PYPNASFD", +"AGFSLWVY", +"IDFLELAM", +"VPNQPYPN", +"PNQPYPNA", +"YPNASFDN", +"NQPYPNAS", +"FEHIVYGD", +"QPYPNASF", +"AFEHIVYG", +"EFIERYKL", +"YAFEHIVY", +"FLELAMDE", +"DFLELAMD", +"YKLEGYAF", +"FIERYKLE", +"KPRSQMEI", +"GYAFEHIV", +"LELAMDEF", +"DEFIERYK", +"ELAMDEFI", +"MDEFIERY", +"KLEGYAFE", +"EGYAFEHI", +"LEGYAFEH", +"DTACSSSL", +"LAMDEFIE", +"AMDEFIER", +"RSQMEIDF", +"SQMEIDFL", +"PRSQMEID", +"ATRGATVV", +"SAGFSLWV", +"GCDGGSLY", +"ISAGFSLW", +"KHAFHTPA", +"MISAGFSL", +"CDGGSLYV", +"NKHAFHTP", +"AATRGATV", +"VNKHAFHT", +"GGSLYVNK", +"SLYVNKHA", +"MMISAGFS", +"GSLYVNKH", +"LYVNKHAF", +"DGGSLYVN", +"YVNKHAFH", +"GFRIELGE", +"RGFRIELG", +"TACSSSLV", +"LKQLPFFY", +"NLKQLPFF", +"FVNLKQLP", +"GAVCRHHA", +"HAFHTPAF", +"VNLKQLPF", +"GGAVCRHH", +"AFHTPAFD", +"KQLPFFYY", +"VCRHHANE", +"AVCRHHAN", +"FHTPAFDK", +"CRHHANEY", +"AFVNLKQL", +"LGGAVCRH", +"QLPFFYYS", +"RHHANEYR", +"SAFVNLKQ", +"KSAFVNLK", +"HTPAFDKS", +"AFDKSAFV", +"TPAFDKSA", +"DKSAFVNL", +"NMMISAGF", +"CNLGGAVC", +"LPFFYYSD", +"NLGGAVCR", +"PAFDKSAF", +"FDKSAFVN", +"HHANEYRL", +"YNMMISAG", +"PFFYYSDS", +"RCNLGGAV", +"AYNMMISA", +"DAYNMMIS", +"HANEYRLY", +"ITRCNLGG", +"LDAYNMMI", +"CITRCNLG", +"ANEYRLYL", +"LYLDAYNM", +"TRCNLGGA", +"CESHGKQV", +"IAATRGAT", +"YLDAYNMM", +"SIAATRGA", +"NEYRLYLD", +"EYRLYLDA", +"YRLYLDAY", +"QFHQKLLK",*/ +}; \ No newline at end of file diff --git a/src/util/tantan.cpp b/src/masking/tantan.cpp similarity index 82% rename from src/util/tantan.cpp rename to src/masking/tantan.cpp index 15b84bce1..de7b1542e 100644 --- a/src/util/tantan.cpp +++ b/src/masking/tantan.cpp @@ -27,13 +27,14 @@ A new repeat-masking method enables specific detection of homologous sequences, #include #include #include "../basic/value.h" +#include "def.h" using Eigen::Array; using Eigen::Dynamic; namespace Util { namespace tantan { namespace DISPATCH_ARCH { -void mask(Letter *seq, +Mask::Ranges mask(Letter *seq, int len, const float **likelihood_ratio_matrix, float p_repeat, @@ -44,13 +45,13 @@ void mask(Letter *seq, constexpr int WINDOW = 50, RESERVE = 50000; if (len == 0) - return; + return {}; thread_local std::array, AMINO_ACID_COUNT> e; thread_local Array pb; thread_local Array scale; Array f(0.0), d, t; - const float b2b = 1 - p_repeat, f2f = 1 - p_repeat_end, b2f0 = p_repeat * (1 - repeat_growth) / (1 - pow(repeat_growth, WINDOW)); + const float b2b = 1.0f - p_repeat, f2f = 1.0f - p_repeat_end, b2f0 = p_repeat * (1.0f - repeat_growth) / (1.0f - pow(repeat_growth, (float)WINDOW)); float b = 1.0; d[WINDOW - 1] = b2f0; @@ -65,7 +66,7 @@ void mask(Letter *seq, const float *l = likelihood_ratio_matrix[i]; float* p = &e[i][len - 1]; for (int j = 0; j < len; ++j) - *(p--) = l[(size_t)seq[j]]; + *(p--) = l[(size_t)letter_mask(seq[j])]; std::fill(e[i].data() + len, e[i].data() + len + WINDOW, (float)0.0); } @@ -76,7 +77,7 @@ void mask(Letter *seq, f *= f2f; f += t; - f *= e[(size_t)seq[i]].template segment(len - i, WINDOW); + f *= e[(size_t)letter_mask(seq[i])].template segment(len - i, WINDOW); b = b * b2b + s * p_repeat_end; if ((i & 15) == 15) { @@ -92,6 +93,7 @@ void mask(Letter *seq, const float z = b * b2b + f.sum() * p_repeat_end; b = b2b; f = p_repeat_end; + Mask::Ranges ranges; for (int i = len - 1; i >= 0; --i) { const float pf = 1 - (pb[i] * b / z); @@ -102,10 +104,13 @@ void mask(Letter *seq, f *= s; } - f *= e[(size_t)seq[i]].template segment(len - i, WINDOW); + f *= e[(size_t)letter_mask(seq[i])].template segment(len - i, WINDOW); - if (pf >= p_mask) - seq[i] = mask_table[(size_t)seq[i]]; + if (pf >= p_mask) { + if (mask_table) + seq[i] = mask_table[(size_t)letter_mask(seq[i])]; + ranges.push_front(i); + } t = f; t *= d; @@ -113,6 +118,7 @@ void mask(Letter *seq, f += p_repeat_end * b; b = b2b * b + t.sum(); } + return ranges; } }}} \ No newline at end of file diff --git a/src/util/tantan.h b/src/masking/tantan.h similarity index 78% rename from src/util/tantan.h rename to src/masking/tantan.h index 3fd3ecec8..adce0da3e 100644 --- a/src/util/tantan.h +++ b/src/masking/tantan.h @@ -18,16 +18,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef TANTAN_H_ -#define TANTAN_H_ - -#include "simd.h" +#pragma once +#include "../util/simd.h" #include "../basic/value.h" namespace Util { namespace tantan { -DECL_DISPATCH(void, mask, (Letter *seq, int len, const float **likelihood_ratio_matrix, float p_repeat, float p_repeat_end, float repeat_decay, float p_mask, const Letter *maskTable)) +DECL_DISPATCH(Mask::Ranges, mask, (Letter *seq, int len, const float **likelihood_ratio_matrix, float p_repeat, float p_repeat_end, float repeat_decay, float p_mask, const Letter *maskTable)) }} - -#endif \ No newline at end of file diff --git a/src/output/blast_pairwise_format.cpp b/src/output/blast_pairwise_format.cpp index 13a6cb962..dcb6dbc92 100644 --- a/src/output/blast_pairwise_format.cpp +++ b/src/output/blast_pairwise_format.cpp @@ -19,13 +19,14 @@ along with this program. If not, see . #include "output_format.h" #include "../util/util.h" -void Pairwise_format::print_match(const HspContext& r, const Search::Config& metadata, TextBuffer &out) +void Pairwise_format::print_match(const HspContext& r, Output::Info &info) { static const unsigned width = 60; + TextBuffer& out = info.out; const int dna_len = (int)r.query.source().length(); const Strand strand = r.frame() < 3 ? FORWARD : REVERSE; out << '>'; - Output_format::print_title(out, r.target_title, true, true, " "); + OutputFormat::print_title(out, r.target_title.c_str(), true, true, " "); out << "\nLength=" << r.subject_len << "\n\n"; out << " Score = " << r.bit_score() << " bits (" << r.score() << "), Expect = "; out.print_e(r.evalue()); @@ -70,15 +71,15 @@ void Pairwise_format::print_footer(Consumer &out) const } -void Pairwise_format::print_query_epilog(TextBuffer &out, const char *query_title, bool unaligned, const Search::Config ¶ms) const +void Pairwise_format::print_query_epilog(Output::Info &info) const { } -void Pairwise_format::print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const +void Pairwise_format::print_query_intro(Output::Info &info) const { - out << "Query= " << query_name << "\n\nLength=" << query_len << "\n\n"; - if (unaligned) { - out << "\n***** No hits found *****\n\n\n"; + info.out << "Query= " << info.query.title << "\n\nLength=" << info.query.len << "\n\n"; + if (info.unaligned) { + info.out << "\n***** No hits found *****\n\n\n"; } } diff --git a/src/output/blast_tab_format.cpp b/src/output/blast_tab_format.cpp index 2639607f0..3914ebef2 100644 --- a/src/output/blast_tab_format.cpp +++ b/src/output/blast_tab_format.cpp @@ -34,85 +34,121 @@ along with this program. If not, see . #include "../util/sequence/sequence.h" #include "../dp/ungapped.h" #include "../basic/reduction.h" +#include "../dp/needleman_wunsch.h" using namespace Output; using std::endl; using std::set; +using std::string; +using std::transform; +using std::back_inserter; +using std::vector; +using std::runtime_error; const vector Blast_tab_format::field_def = { -{ "qseqid", "Query ID", HspValues::NONE, Flags::NONE }, // 0 means Query Seq - id -{ "qgi", "qgi", HspValues::NONE, Flags::NONE }, // 1 means Query GI -{ "qacc", "qacc", HspValues::NONE, Flags::NONE }, // 2 means Query accesion -{ "qaccver", "qaccver", HspValues::NONE, Flags::NONE }, // 3 means Query accesion.version -{ "qlen", "Query length", HspValues::NONE, Flags::NONE }, // 4 means Query sequence length -{ "sseqid", "Subject ID", HspValues::NONE, Flags::NONE }, // 5 means Subject Seq - id -{ "sallseqid", "Subject IDs", HspValues::NONE, Flags::ALL_SEQIDS }, // 6 means All subject Seq - id(s), separated by a ';' -{ "sgi", "sgi", HspValues::NONE, Flags::NONE }, // 7 means Subject GI -{ "sallgi", "sallgi", HspValues::NONE, Flags::ALL_SEQIDS }, // 8 means All subject GIs -{ "sacc", "sacc", HspValues::NONE, Flags::NONE }, // 9 means Subject accession -{ "saccver", "saccver", HspValues::NONE, Flags::NONE }, // 10 means Subject accession.version -{ "sallacc", "sallacc", HspValues::NONE, Flags::ALL_SEQIDS }, // 11 means All subject accessions -{ "slen", "Subject length", HspValues::NONE, Flags::NONE }, // 12 means Subject sequence length -{ "qstart", "Start of alignment in query", HspValues::QUERY_START, Flags::NONE }, // 13 means Start of alignment in query -{ "qend", "End of alignment in query", HspValues::QUERY_END, Flags::NONE }, // 14 means End of alignment in query -{ "sstart", "Start of alignment in subject", HspValues::TARGET_START, Flags::NONE }, // 15 means Start of alignment in subject -{ "send", "End of alignment in subject", HspValues::TARGET_END, Flags::NONE }, // 16 means End of alignment in subject -{ "qseq", "Aligned part of query sequence", HspValues::QUERY_COORDS, Flags::NONE }, // 17 means Aligned part of query sequence -{ "sseq", "Aligned part of subject sequence", HspValues::TRANSCRIPT, Flags::NONE }, // 18 means Aligned part of subject sequence -{ "evalue", "Expected value", HspValues::NONE, Flags::NONE }, // 19 means Expect value -{ "bitscore", "Bit score", HspValues::NONE, Flags::NONE }, // 20 means Bit score -{ "score", "Raw score", HspValues::NONE, Flags::NONE }, // 21 means Raw score -{ "length", "Alignment length", HspValues::LENGTH, Flags::NONE }, // 22 means Alignment length -{ "pident", "Percentage of identical matches", HspValues::IDENT | HspValues::LENGTH, Flags::NONE }, // 23 means Percentage of identical matches -{ "nident", "Number of identical matches", HspValues::IDENT, Flags::NONE }, // 24 means Number of identical matches -{ "mismatch", "Number of mismatches", HspValues::MISMATCHES, Flags::NONE }, // 25 means Number of mismatches -{ "positive", "Number of positive-scoring matches", HspValues::TRANSCRIPT, Flags::NONE }, // 26 means Number of positive - scoring matches -{ "gapopen", "Number of gap openings", HspValues::GAP_OPENINGS, Flags::NONE }, // 27 means Number of gap openings -{ "gaps", "Total number of gaps", HspValues::GAPS, Flags::NONE }, // 28 means Total number of gaps -{ "ppos", "Percentage of positive-scoring matches", HspValues::TRANSCRIPT, Flags::NONE }, // 29 means Percentage of positive - scoring matches -{ "frames", "frames", HspValues::NONE, Flags::NONE }, // 30 means Query and subject frames separated by a '/' -{ "qframe", "Query frame", HspValues::NONE, Flags::NONE }, // 31 means Query frame -{ "sframe", "sframe", HspValues::NONE, Flags::NONE }, // 32 means Subject frame -{ "btop", "Blast traceback operations", HspValues::TRANSCRIPT, Flags::NONE }, // 33 means Blast traceback operations(BTOP) -{ "staxids", "Subject Taxonomy IDs", HspValues::NONE, Flags::NONE }, // 34 means unique Subject Taxonomy ID(s), separated by a ';' (in numerical order) -{ "sscinames", "Subject scientific names", HspValues::NONE, Flags::NONE }, // 35 means unique Subject Scientific Name(s), separated by a ';' -{ "scomnames", "scomnames", HspValues::NONE, Flags::NONE }, // 36 means unique Subject Common Name(s), separated by a ';' -{ "sblastnames", "sblastnames", HspValues::NONE, Flags::NONE }, // 37 means unique Subject Blast Name(s), separated by a ';' (in alphabetical order) -{ "sskingdoms", "Subject super kingdoms", HspValues::NONE, Flags::NONE }, // 38 means unique Subject Super Kingdom(s), separated by a ';' (in alphabetical order) -{ "stitle", "Subject title", HspValues::NONE, Flags::FULL_TITLES }, // 39 means Subject Title -{ "salltitles", "Subject titles", HspValues::NONE, Flags::FULL_TITLES }, // 40 means All Subject Title(s), separated by a '<>' -{ "sstrand", "sstrand", HspValues::NONE, Flags::NONE }, // 41 means Subject Strand -{ "qcovs", "qcovs", HspValues::NONE, Flags::NONE }, // 42 means Query Coverage Per Subject -{ "qcovhsp", "Query coverage per HSP", HspValues::QUERY_COORDS, Flags::NONE }, // 43 means Query Coverage Per HSP -{ "qcovus", "qcovus", HspValues::NONE, Flags::NONE }, // 44 means Query Coverage Per Unique Subject(blastn only) -{ "qtitle", "Query title", HspValues::NONE, Flags::NONE }, // 45 means Query title -{ "swdiff", "swdiff", HspValues::NONE, Flags::NONE }, // 46 -{ "time", "time", HspValues::NONE, Flags::NONE }, // 47 -{ "full_sseq", "Subject sequence", HspValues::NONE, Flags::NONE }, // 48 -{ "qqual", "Aligned part of query quality values", HspValues::QUERY_COORDS, Flags::NONE }, // 49 -{ "qnum", "qnum", HspValues::NONE, Flags::NONE }, // 50 -{ "snum", "snum", HspValues::NONE, Flags::NONE }, // 51 -{ "scovhsp", "Subject coverage per HSP", HspValues::TARGET_COORDS, Flags::NONE }, // 52 -{ "full_qqual", "Query quality values", HspValues::NONE, Flags::NONE }, // 53 -{ "full_qseq", "Query sequence", HspValues::NONE, Flags::NONE }, // 54 -{ "qseq_gapped", "Query sequence with gaps", HspValues::TRANSCRIPT, Flags::NONE }, // 55 -{ "sseq_gapped", "Subject sequence with gaps", HspValues::TRANSCRIPT, Flags::NONE }, // 56 -{ "qstrand", "Query strand", HspValues::NONE, Flags::NONE }, // 57 -{ "cigar", "CIGAR", HspValues::TRANSCRIPT, Flags::NONE }, // 58 -{ "skingdoms", "Subject kingdoms", HspValues::NONE, Flags::NONE }, // 59 -{ "sphylums", "Subject phylums", HspValues::NONE, Flags::NONE }, // 60 -{ "ungapped_score", "Ungapped score", HspValues::NONE, Flags::NONE }, // 61 -{ "full_qseq_mate", "Query sequence of the mate", HspValues::NONE, Flags::NONE }, // 62 -{ "qseq_translated", "Aligned part of query sequence (translated)", HspValues::TRANSCRIPT, Flags::NONE }, // 63 needs transcript only in frameshift mode -{ "reduced_match_bitstring", "", HspValues::TRANSCRIPT, Flags::NONE}, // 64 -{ "normalized_bitscore_semiglobal", "", HspValues::NONE, Flags::NONE}, // 65 -{ "hspnum", "", HspValues::NONE, Flags::NONE}, // 66 -{ "normalized_bitscore_global", "", HspValues::NONE, Flags::SELF_ALN_SCORES}, // 67 -{ "pident_global", "", HspValues::IDENT, Flags::NONE }, // 68 +{ "qseqid", "cseqid", "Query ID", HspValues::NONE, Flags::NONE }, // 0 means Query Seq - id +{ "qgi", "", "qgi", HspValues::NONE, Flags::NONE }, // 1 means Query GI +{ "qacc", "", "qacc", HspValues::NONE, Flags::NONE }, // 2 means Query accesion +{ "qaccver", "", "qaccver", HspValues::NONE, Flags::NONE }, // 3 means Query accesion.version +{ "qlen", "clen", "Query length", HspValues::NONE, Flags::NONE }, // 4 means Query sequence length +{ "sseqid", "mseqid", "Subject ID", HspValues::NONE, Flags::NONE }, // 5 means Subject Seq - id +{ "sallseqid", "", "Subject IDs", HspValues::NONE, Flags::ALL_SEQIDS }, // 6 means All subject Seq - id(s), separated by a ';' +{ "sgi", "", "sgi", HspValues::NONE, Flags::NONE }, // 7 means Subject GI +{ "sallgi", "", "sallgi", HspValues::NONE, Flags::ALL_SEQIDS }, // 8 means All subject GIs +{ "sacc", "", "sacc", HspValues::NONE, Flags::NONE }, // 9 means Subject accession +{ "saccver", "", "saccver", HspValues::NONE, Flags::NONE }, // 10 means Subject accession.version +{ "sallacc", "", "sallacc", HspValues::NONE, Flags::ALL_SEQIDS }, // 11 means All subject accessions +{ "slen", "mlen", "Subject length", HspValues::NONE, Flags::NONE }, // 12 means Subject sequence length +{ "qstart", "cstart", "Start of alignment in query", HspValues::QUERY_START, Flags::NONE }, // 13 means Start of alignment in query +{ "qend", "cend", "End of alignment in query", HspValues::QUERY_END, Flags::NONE }, // 14 means End of alignment in query +{ "sstart", "mstart", "Start of alignment in subject", HspValues::TARGET_START, Flags::NONE }, // 15 means Start of alignment in subject +{ "send", "mend", "End of alignment in subject", HspValues::TARGET_END, Flags::NONE }, // 16 means End of alignment in subject +{ "qseq", "", "Aligned part of query sequence", HspValues::QUERY_COORDS, Flags::NONE }, // 17 means Aligned part of query sequence +{ "sseq", "", "Aligned part of subject sequence", HspValues::TRANSCRIPT, Flags::NONE }, // 18 means Aligned part of subject sequence +{ "evalue", "evalue", "Expected value", HspValues::NONE, Flags::NONE }, // 19 means Expect value +{ "bitscore", "bitscore", "Bit score", HspValues::NONE, Flags::NONE }, // 20 means Bit score +{ "score", "score", "Raw score", HspValues::NONE, Flags::NONE }, // 21 means Raw score +{ "length", "length", "Alignment length", HspValues::LENGTH, Flags::NONE }, // 22 means Alignment length +{ "pident", "pident" ,"Percentage of identical matches", HspValues::IDENT | HspValues::LENGTH, Flags::NONE }, // 23 means Percentage of identical matches +{ "nident", "nident", "Number of identical matches", HspValues::IDENT, Flags::NONE }, // 24 means Number of identical matches +{ "mismatch", "mismatch", "Number of mismatches", HspValues::MISMATCHES, Flags::NONE }, // 25 means Number of mismatches +{ "positive", "positive", "Number of positive-scoring matches", HspValues::TRANSCRIPT, Flags::NONE }, // 26 means Number of positive - scoring matches +{ "gapopen", "gapopen", "Number of gap openings", HspValues::GAP_OPENINGS, Flags::NONE }, // 27 means Number of gap openings +{ "gaps", "gaps", "Total number of gaps", HspValues::GAPS, Flags::NONE }, // 28 means Total number of gaps +{ "ppos", "ppos", "Percentage of positive-scoring matches", HspValues::TRANSCRIPT, Flags::NONE }, // 29 means Percentage of positive - scoring matches +{ "frames", "", "frames", HspValues::NONE, Flags::NONE }, // 30 means Query and subject frames separated by a '/' +{ "qframe", "", "Query frame", HspValues::NONE, Flags::NONE }, // 31 means Query frame +{ "sframe", "", "sframe", HspValues::NONE, Flags::NONE }, // 32 means Subject frame +{ "btop", "", "Blast traceback operations", HspValues::TRANSCRIPT, Flags::NONE }, // 33 means Blast traceback operations(BTOP) +{ "staxids", "", "Subject Taxonomy IDs", HspValues::NONE, Flags::NONE }, // 34 means unique Subject Taxonomy ID(s), separated by a ';' (in numerical order) +{ "sscinames", "", "Subject scientific names", HspValues::NONE, Flags::NONE }, // 35 means unique Subject Scientific Name(s), separated by a ';' +{ "scomnames", "", "scomnames", HspValues::NONE, Flags::NONE }, // 36 means unique Subject Common Name(s), separated by a ';' +{ "sblastnames", "", "sblastnames", HspValues::NONE, Flags::NONE }, // 37 means unique Subject Blast Name(s), separated by a ';' (in alphabetical order) +{ "sskingdoms", "", "Subject super kingdoms", HspValues::NONE, Flags::NONE }, // 38 means unique Subject Super Kingdom(s), separated by a ';' (in alphabetical order) +{ "stitle", "", "Subject title", HspValues::NONE, Flags::FULL_TITLES }, // 39 means Subject Title +{ "salltitles", "", "Subject titles", HspValues::NONE, Flags::FULL_TITLES }, // 40 means All Subject Title(s), separated by a '<>' +{ "sstrand", "", "sstrand", HspValues::NONE, Flags::NONE }, // 41 means Subject Strand +{ "qcovs", "", "qcovs", HspValues::NONE, Flags::NONE }, // 42 means Query Coverage Per Subject +{ "qcovhsp", "ccovhsp", "Query coverage per HSP", HspValues::QUERY_COORDS, Flags::NONE }, // 43 means Query Coverage Per HSP +{ "qcovus", "", "qcovus", HspValues::NONE, Flags::NONE }, // 44 means Query Coverage Per Unique Subject(blastn only) +{ "qtitle", "", "Query title", HspValues::NONE, Flags::NONE }, // 45 means Query title +{ "swdiff", "", "swdiff", HspValues::NONE, Flags::NONE }, // 46 +{ "time", "", "time", HspValues::NONE, Flags::NONE }, // 47 +{ "full_sseq", "", "Subject sequence", HspValues::NONE, Flags::NONE }, // 48 +{ "qqual", "", "Aligned part of query quality values", HspValues::QUERY_COORDS, Flags::NONE }, // 49 +{ "qnum", "", "qnum", HspValues::NONE, Flags::NONE }, // 50 +{ "snum", "", "snum", HspValues::NONE, Flags::NONE }, // 51 +{ "scovhsp", "mcovhsp", "Subject coverage per HSP", HspValues::TARGET_COORDS, Flags::NONE }, // 52 +{ "full_qqual", "", "Query quality values", HspValues::NONE, Flags::NONE }, // 53 +{ "full_qseq", "", "Query sequence", HspValues::NONE, Flags::NONE }, // 54 +{ "qseq_gapped", "", "Query sequence with gaps", HspValues::TRANSCRIPT, Flags::NONE }, // 55 +{ "sseq_gapped", "", "Subject sequence with gaps", HspValues::TRANSCRIPT, Flags::NONE }, // 56 +{ "qstrand", "", "Query strand", HspValues::NONE, Flags::NONE }, // 57 +{ "cigar", "", "CIGAR", HspValues::TRANSCRIPT, Flags::NONE }, // 58 +{ "skingdoms", "", "Subject kingdoms", HspValues::NONE, Flags::NONE }, // 59 +{ "sphylums", "", "Subject phylums", HspValues::NONE, Flags::NONE }, // 60 +{ "ungapped_score", "", "Ungapped score", HspValues::NONE, Flags::NONE }, // 61 +{ "full_qseq_mate", "", "Query sequence of the mate", HspValues::NONE, Flags::NONE }, // 62 +{ "qseq_translated", "", "Aligned part of query sequence (translated)", HspValues::TRANSCRIPT, Flags::NONE }, // 63 needs transcript only in frameshift mode +{ "reduced_match_bitstring", "", "", HspValues::TRANSCRIPT, Flags::NONE}, // 64 +{ "normalized_bitscore_semiglobal", "", "", HspValues::NONE, Flags::NONE}, // 65 +{ "hspnum", "", "", HspValues::NONE, Flags::NONE}, // 66 +{ "normalized_bitscore_global", "", "", HspValues::NONE, Flags::SELF_ALN_SCORES}, // 67 +{ "pident_global", "", "", HspValues::IDENT, Flags::NONE }, // 68 +{ "ext_count", "", "", HspValues::NONE, Flags::NONE }, // 69 +{ "nw_semiglobal", "", "", HspValues::NONE, Flags::NONE }, // 70 +{ "approx_pident", "approx_pident", "", HspValues::COORDS, Flags::NONE }, // 71 +{ "corrected_bitscore", "corrected_bitscore", "", HspValues::NONE, Flags::NONE }, // 72 +{ "neg_evalue", "neg_evalue", "", HspValues::NONE, Flags::NONE }, // 73 +{ "reserved1", "reserved1", "", HspValues::NONE, Flags::NONE}, // 74 +{ "reserved2", "reserved2", "", HspValues::NONE, Flags::NONE} // 75 }; +template<> +struct EnumTraits
{ + static const SEMap
from_string; +}; + +const SEMap
EnumTraits
::from_string = { {"0", Header::NONE}, {"simple", Header::SIMPLE}, {"verbose", Header::VERBOSE} }; + +Header Blast_tab_format::header_format(unsigned workflow) { + const bool cluster = workflow == Config::cluster || workflow == Config::DEEPCLUST; + if (workflow != Config::blastp && !cluster) + throw runtime_error("header_format"); + if (!config.output_header.present()) + return Header::NONE; + if (config.output_header.empty()) + return cluster ? Header::SIMPLE : Header::VERBOSE; + if (config.output_header.size() > 1) + throw runtime_error("Invalid header format: " + join(" ", config.output_header)); + const Header h = from_string
(config.output_header.front()); + if (h == Header::VERBOSE && cluster) + throw runtime_error("Verbose header format is not supported for cluster workflow."); + return h; +} + Blast_tab_format::Blast_tab_format() : - Output_format(blast_tab, HspValues::NONE, Flags::NONE) + OutputFormat(blast_tab, HspValues::NONE, Flags::NONE) { static const unsigned stdf[] = { 0, 5, 23, 22, 25, 27, 13, 14, 15, 16, 19, 20 }; const vector &f = config.output_format; @@ -142,12 +178,14 @@ Blast_tab_format::Blast_tab_format() : fields.push_back(j); if (j == 6 || j == 39 || j == 40 || j == 34) config.salltitles = true; - if (j == 48 || j == 65) + if (j == 48 || j == 65 || j == 71) flags |= Flags::TARGET_SEQS; if (j == 49 || j == 53) config.store_query_quality = true; if (j == 62) needs_paired_end_info = true; + if (j == 71 && score_matrix.name() != "BLOSUM62") + throw std::runtime_error("Approximate identity is only supported for the BLOSUM62 scoring matrix."); if ((j == 62 || j == 63) && !align_mode.query_translated) throw std::runtime_error("Output field only supported for translated search."); hsp_values |= field_def[j].hsp_values; @@ -157,44 +195,40 @@ Blast_tab_format::Blast_tab_format() : //config.traceback_mode = TracebackMode::SCORE_ONLY; } -void print_staxids(TextBuffer &out, unsigned subject_global_id, const Search::Config& cfg) +void print_staxids(TextBuffer &out, int64_t subject_global_id, const SequenceFile& db) { - out.print(cfg.db->taxids(subject_global_id), ';'); + out.print(db.taxids(subject_global_id), ';'); } template -void print_taxon_names(_it begin, _it end, const Search::Config& cfg, TextBuffer &out) { +void print_taxon_names(_it begin, _it end, const SequenceFile& db, TextBuffer &out) { if (begin == end) { out << "N/A"; return; } - const vector &names = *cfg.taxonomy_scientific_names; for (_it i = begin; i != end; ++i) { if (i != begin) out << ';'; - if (*i < names.size() && !names[*i].empty()) - out << names[*i]; - else - out << *i; + out << db.taxon_scientific_name(*i); } } -void Blast_tab_format::print_match(const HspContext& r, const Search::Config& cfg, TextBuffer &out) +void Blast_tab_format::print_match(const HspContext& r, Output::Info& info) { - const StringSet& query_qual = cfg.query->qual(); + TextBuffer& out = info.out; for (auto i = fields.cbegin(); i != fields.cend(); ++i) { switch (*i) { case 0: - out.write_until(r.query_title, Util::Seq::id_delimiters); + out.write_until(r.query_title.c_str(), Util::Seq::id_delimiters); break; case 4: out << r.query.source().length(); break; case 5: - print_title(out, r.target_title, false, false, "<>"); + print_title(out, r.target_title.c_str(), false, false, "<>"); break; case 6: - print_title(out, r.target_title, false, true, "<>"); + print_title(out, r.target_title.c_str(), false, true, "<>"); break; case 12: out << r.subject_len; @@ -237,7 +271,7 @@ void Blast_tab_format::print_match(const HspContext& r, const Search::Config& cf out << r.length(); break; case 23: - out << (double)r.identities() * 100 / r.length(); + out << r.id_percent(); break; case 24: out << r.identities(); @@ -296,28 +330,28 @@ void Blast_tab_format::print_match(const HspContext& r, const Search::Config& cf if (n_matches > 0) out << n_matches; } - break; + break; case 34: - print_staxids(out, r.subject_oid, cfg); + print_staxids(out, r.subject_oid, *info.db); break; case 35: { - const vector tax_id = cfg.db->taxids(r.subject_oid); - print_taxon_names(tax_id.begin(), tax_id.end(), cfg, out); + const vector tax_id = info.db->taxids(r.subject_oid); + print_taxon_names(tax_id.begin(), tax_id.end(), *info.db, out); break; } case 38: { - const set tax_id = cfg.taxon_nodes->rank_taxid(cfg.db->taxids(r.subject_oid), Rank::superkingdom); - print_taxon_names(tax_id.begin(), tax_id.end(), cfg, out); + const set tax_id = info.db->taxon_nodes().rank_taxid(info.db->taxids(r.subject_oid), Rank::superkingdom); + print_taxon_names(tax_id.begin(), tax_id.end(), *info.db, out); break; } case 39: - print_title(out, r.target_title, true, false, "<>"); + print_title(out, r.target_title.c_str(), true, false, "<>"); break; case 40: - print_title(out, r.target_title, true, true, "<>"); + print_title(out, r.target_title.c_str(), true, true, "<>"); break; case 43: - out << (double)r.query_source_range().length()*100.0 / r.query.source().length(); + out << r.qcovhsp(); break; case 45: out << r.query_title; @@ -332,30 +366,24 @@ void Blast_tab_format::print_match(const HspContext& r, const Search::Config& cf out << r.subject_seq; break; case 49: { - if (cfg.query->qual().empty()) { - out << '*'; - break; - } - const char *q = cfg.query->qual()[r.query_id]; - if (strlen(q) == 0) { + if (strlen(info.query.qual) == 0) { out << '*'; break; } - out << string(q + r.query_source_range().begin_, q + r.query_source_range().end_).c_str(); + out << string(info.query.qual + r.query_source_range().begin_, info.query.qual + r.query_source_range().end_).c_str(); break; } case 50: - out << cfg.query->block_id2oid(r.query_id); - //out << r.query_id; + out << r.query_oid; break; case 51: out << r.subject_oid; break; case 52: - out << (double)r.subject_range().length() * 100.0 / r.subject_len; + out << r.scovhsp(); break; case 53: - out << (!query_qual.empty() && strlen(query_qual[r.query_id]) ? query_qual[r.query_id] : "*"); + out << strlen(info.query.qual) ? info.query.qual : "*"; break; case 54: r.query.source().print(out, input_value_traits); @@ -378,13 +406,13 @@ void Blast_tab_format::print_match(const HspContext& r, const Search::Config& cf print_cigar(r, out); break; case 59: { - const set tax_id = cfg.taxon_nodes->rank_taxid(cfg.db->taxids(r.subject_oid), Rank::kingdom); - print_taxon_names(tax_id.begin(), tax_id.end(), cfg, out); + const set tax_id = info.db->taxon_nodes().rank_taxid(info.db->taxids(r.subject_oid), Rank::kingdom); + print_taxon_names(tax_id.begin(), tax_id.end(), *info.db, out); break; } case 60: { - const set tax_id = cfg.taxon_nodes->rank_taxid(cfg.db->taxids(r.subject_oid), Rank::phylum); - print_taxon_names(tax_id.begin(), tax_id.end(), cfg, out); + const set tax_id = info.db->taxon_nodes().rank_taxid(info.db->taxids(r.subject_oid), Rank::phylum); + print_taxon_names(tax_id.begin(), tax_id.end(), *info.db, out); break; } case 61: @@ -392,8 +420,7 @@ void Blast_tab_format::print_match(const HspContext& r, const Search::Config& cf break; case 62: { if (config.query_file.size() == 2) { - unsigned mate = r.query_id % 2, mate_id = mate == 0 ? r.query_id + 1 : r.query_id - 1; - cfg.query->source_seqs()[mate_id].print(out, input_value_traits); + info.query.mate_seq.print(out, input_value_traits); break; } else { @@ -440,6 +467,12 @@ void Blast_tab_format::print_match(const HspContext& r, const Search::Config& cf out << s; break; } + case 71: + out << r.approx_id(); + break; + case 72: + out << r.corrected_bit_score(); + break; #ifdef EXTRA case 65: out.print_d(r.bit_score() / score_matrix.bitscore(self_score(r.subject_seq))); @@ -453,6 +486,23 @@ void Blast_tab_format::print_match(const HspContext& r, const Search::Config& cf case 68: out << (double)r.identities() / std::max(r.query.index(r.frame()).length(), r.subject_len) * 100; break; + case 69: + out << info.stats.extension_count; + break; + case 70: + out.print_e(score_matrix.bitscore(r.query[Frame(0)].length() < r.subject_len ? + nw_semiglobal(r.query[Frame(0)], r.subject_seq) + : nw_semiglobal(r.subject_seq, r.query[Frame(0)]))); + break; + case 73: + out.print_e(r.evalue() == 0 ? r.bit_score() : -r.evalue()); + break; + case 74: + out << r.reserved1(); + break; + case 75: + out << r.reserved2(); + break; #endif default: throw std::runtime_error(string("Invalid output field: ") + field_def[*i].key); @@ -463,17 +513,17 @@ void Blast_tab_format::print_match(const HspContext& r, const Search::Config& cf out << '\n'; } -void Blast_tab_format::print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const +void Blast_tab_format::print_query_intro(Output::Info& info) const { - const StringSet& qual = cfg.query->qual(); - if (unaligned && config.report_unaligned == 1) { + TextBuffer& out = info.out; + if (info.unaligned && config.report_unaligned == 1) { for (auto i = fields.cbegin(); i != fields.cend(); ++i) { switch (*i) { case 0: - out.write_until(query_name, Util::Seq::id_delimiters); + out.write_until(info.query.title, Util::Seq::id_delimiters); break; case 4: - out << query_len; + out << info.query.len; break; case 5: case 6: @@ -514,6 +564,7 @@ void Blast_tab_format::print_query_intro(size_t query_num, const char *query_nam case 43: case 52: case 61: + case 71: out << "-1"; break; case 31: @@ -521,13 +572,13 @@ void Blast_tab_format::print_query_intro(size_t query_num, const char *query_nam out << '0'; break; case 45: - out << query_name; + out << info.query.title; break; case 53: - out << (!qual.empty() && strlen(qual[query_num]) ? qual[query_num] : "*"); + out << strlen(info.query.qual) ? info.query.qual : "*"; break; case 54: - (align_mode.query_translated ? cfg.query->source_seqs()[query_num] : cfg.query->seqs()[query_num]).print(out, input_value_traits); + info.query.source_seq.print(out, input_value_traits); break; default: throw std::runtime_error(string("Invalid output field: ") + field_def[*i].key); @@ -539,13 +590,30 @@ void Blast_tab_format::print_query_intro(size_t query_num, const char *query_nam } } -void Blast_tab_format::print_header(Consumer &f, int mode, const char *matrix, int gap_open, int gap_extend, double evalue, const char *first_query_name, unsigned first_query_len) const { - if (!config.output_header) - return; - std::stringstream ss; - ss << "# DIAMOND v" << Const::version_string << ". http://github.com/bbuchfink/diamond" << endl; - ss << "# Invocation: " << config.invocation << endl; - ss << "# Fields: " << join(", ", apply(fields, [](int64_t i) -> string { return string(field_def[i].description); })) << endl; - const string s(ss.str()); +void Blast_tab_format::output_header(Consumer& f, bool cluster) const { + vector headers; + transform(fields.begin(), fields.end(), back_inserter(headers), [cluster](int64_t i) { + const string& key = cluster ? Blast_tab_format::field_def[i].clust_key : Blast_tab_format::field_def[i].key; + if (cluster && key.empty()) + throw runtime_error("Output field not supported for clustering: " + Blast_tab_format::field_def[i].key); + return key; + }); + const string s = join("\t", headers) + '\n'; f.consume(s.data(), s.length()); + return; +} + +void Blast_tab_format::print_header(Consumer &f, int mode, const char *matrix, int gap_open, int gap_extend, double evalue, const char *first_query_name, unsigned first_query_len) const { + const Header h = header_format(Config::blastp); + if (h == Header::VERBOSE) { + std::stringstream ss; + ss << "# DIAMOND v" << Const::version_string << ". http://github.com/bbuchfink/diamond" << endl; + ss << "# Invocation: " << config.invocation << endl; + ss << "# Fields: " << join(", ", apply(fields, [](int64_t i) -> string { return string(field_def[i].description); })) << endl; + const string s(ss.str()); + f.consume(s.data(), s.length()); + } + else if (h == Header::SIMPLE) { + output_header(f, false); + } } \ No newline at end of file diff --git a/src/output/clustering_format.cpp b/src/output/clustering_format.cpp index 59cf81111..4e4b1fa81 100644 --- a/src/output/clustering_format.cpp +++ b/src/output/clustering_format.cpp @@ -19,16 +19,28 @@ along with this program. If not, see . #include "output_format.h" #include "../data/queries.h" +#include "recursive_parser.h" -void Clustering_format::print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const -{ -} +using std::string; -void Clustering_format::print_match(const HspContext& r, const Search::Config &metadata, TextBuffer &out) +void Clustering_format::print_match(const HspContext& r, Output::Info& info) { - out.write((uint32_t) metadata.query->block_id2oid(r.query_id)); - out.write((uint32_t) r.subject_oid); - RecursiveParser rp(&r, format.c_str(), false); + info.out.write((uint32_t) r.query_oid); + info.out.write((uint32_t) r.subject_oid); + RecursiveParser rp(&r, format.c_str()); const double res = rp.evaluate(); - out.write(res); + info.out.write(res); +} + +Clustering_format::Clustering_format(const string* const format): + OutputFormat(bin1, HspValues::NONE), + format(RecursiveParser::clean_expression(format)) +{ + RecursiveParser rp(nullptr, format->c_str()); + rp.evaluate(); + const auto vars = rp.variables(); + for (const Variable* v : vars) { + this->hsp_values |= v->hsp_values; + this->flags |= v->flags; + } } diff --git a/src/output/clustering_variables.h b/src/output/clustering_variables.h index 078c9d754..a2c420559 100644 --- a/src/output/clustering_variables.h +++ b/src/output/clustering_variables.h @@ -21,11 +21,20 @@ along with this program. If not, see . #include #include "../stats/score_matrix.h" #include "../basic/match.h" +#include "def.h" +#include "../dp/flags.h" +#include "def.h" class Variable{ public: - virtual double get(const HspContext& r) = 0; + Variable(const HspValues hsp_values = HspValues::NONE, const Output::Flags flags = Output::Flags::NONE): + hsp_values(hsp_values), + flags(flags) + {} + virtual double get(const HspContext& r) const = 0; virtual ~Variable(){}; + const HspValues hsp_values; + const Output::Flags flags; }; class QueryLength: public Variable{ @@ -33,7 +42,7 @@ class QueryLength: public Variable{ static const std::string get_name(){ return "qlen"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.query.source().length(); } }; @@ -42,43 +51,55 @@ class SubjectLength: public Variable{ static const std::string get_name(){ return "slen"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.subject_len; } }; class QueryStart: public Variable{ public: + QueryStart(): + Variable(HspValues::QUERY_START) + {} static const std::string get_name(){ return "qstart"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.oriented_query_range().begin_ + 1; } }; class QueryEnd: public Variable{ public: + QueryEnd() : + Variable(HspValues::QUERY_END) + {} static const std::string get_name(){ return "qend"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.oriented_query_range().end_ + 1; } }; class SubjectStart: public Variable{ public: + SubjectStart() : + Variable(HspValues::TARGET_START) + {} static const std::string get_name(){ return "sstart"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.subject_range().begin_ + 1; } }; class SubjectEnd: public Variable{ public: + SubjectEnd() : + Variable(HspValues::TARGET_END) + {} static const std::string get_name(){ return "send"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.subject_range().end_; } }; @@ -87,7 +108,7 @@ class EValue: public Variable{ static const std::string get_name(){ return "evalue"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.evalue(); } }; @@ -96,88 +117,112 @@ class BitScore: public Variable{ static const std::string get_name(){ return "bitscore"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.bit_score(); } }; -class Score: public Variable{ +class RawScore: public Variable{ public: static const std::string get_name(){ return "score"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.score(); } }; class Length: public Variable{ public: + Length(): + Variable(HspValues::LENGTH) + {} static const std::string get_name(){ return "length"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.length(); } }; class PercentIdenticalMatches: public Variable{ public: + PercentIdenticalMatches(): + Variable(HspValues::LENGTH | HspValues::IDENT) + {} static const std::string get_name(){ return "pident"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return (double)r.identities() * 100 / r.length(); } }; class NumberIdenticalMatches: public Variable{ public: + NumberIdenticalMatches() : + Variable(HspValues::IDENT) + {} static const std::string get_name(){ return "nident"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.identities(); } }; class NumberMismatches: public Variable{ public: + NumberMismatches() : + Variable(HspValues::MISMATCHES) + {} static const std::string get_name(){ return "mismatch"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.mismatches(); } }; class NumberPositiveMatches: public Variable{ public: + NumberPositiveMatches() : + Variable(HspValues::TRANSCRIPT) + {} static const std::string get_name(){ return "positive"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.positives(); } }; class NumberGapOpenings: public Variable{ public: + NumberGapOpenings() : + Variable(HspValues::GAP_OPENINGS) + {} static const std::string get_name(){ return "gapopen"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.gap_openings(); } }; class NumberGaps: public Variable{ public: + NumberGaps() : + Variable(HspValues::GAPS) + {} static const std::string get_name(){ return "gaps"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.gaps(); } }; class PercentagePositiveMatches: public Variable{ public: + PercentagePositiveMatches() : + Variable(HspValues::TRANSCRIPT) + {} static const std::string get_name(){ return "ppos"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return (double)r.positives() * 100.0 / r.length(); } }; @@ -186,35 +231,44 @@ class QueryFrame: public Variable{ static const std::string get_name(){ return "qframe"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return r.blast_query_frame(); } }; class QueryCoveragePerHsp: public Variable{ public: + QueryCoveragePerHsp() : + Variable(HspValues::QUERY_COORDS) + {} static const std::string get_name(){ return "qcovhsp"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return (double)r.query_source_range().length()*100.0 / r.query.source().length(); } }; class SubjectCoveragePerHsp: public Variable{ public: + SubjectCoveragePerHsp() : + Variable(HspValues::TARGET_COORDS) + {} static const std::string get_name(){ return "scovhsp"; } - double get(const HspContext& r){ + virtual double get(const HspContext& r) const override { return (double)r.subject_range().length() * 100.0 / r.subject_len; } }; -class UngappedScore: public Variable{ -public: - static const std::string get_name(){ - return "ungapped_score"; + +struct NormalizedBitScoreGlobal : public Variable { + NormalizedBitScoreGlobal(): + Variable(HspValues::NONE, Output::Flags::SELF_ALN_SCORES) + {} + static const std::string get_name() { + return "normalized_bitscore_global"; } - double get(const HspContext& r){ - return score_matrix.bitscore(r.ungapped_score); + virtual double get(const HspContext& r) const override { + return r.bit_score() / std::max(r.query_self_aln_score, r.target_self_aln_score) * 100; } }; @@ -231,7 +285,7 @@ class StaticVariableRegistry{ regMap[SubjectEnd::get_name()] = new SubjectEnd(); regMap[EValue::get_name()] = new EValue(); regMap[BitScore::get_name()] = new BitScore(); - regMap[Score::get_name()] = new Score(); + regMap[RawScore::get_name()] = new RawScore(); regMap[Length::get_name()] = new Length(); regMap[PercentIdenticalMatches::get_name()] = new PercentIdenticalMatches(); regMap[NumberIdenticalMatches::get_name()] = new NumberIdenticalMatches(); @@ -243,7 +297,7 @@ class StaticVariableRegistry{ regMap[QueryFrame::get_name()] = new QueryFrame(); regMap[QueryCoveragePerHsp::get_name()] = new QueryCoveragePerHsp(); regMap[SubjectCoveragePerHsp::get_name()] = new SubjectCoveragePerHsp(); - regMap[UngappedScore::get_name()] = new UngappedScore(); + regMap[NormalizedBitScoreGlobal::get_name()] = new NormalizedBitScoreGlobal(); } ~StaticVariableRegistry(){ for(auto it = regMap.begin(); it != regMap.end(); it++){ @@ -261,9 +315,9 @@ class StaticVariableRegistry{ bool has(std::string key) const { return regMap.find(key) != regMap.end(); } - vector getKeys() const { + std::vector getKeys() const { auto it = regMap.begin(); - vector keys; + std::vector keys; while(it != regMap.end()){ keys.push_back(it->first); it++; @@ -281,7 +335,7 @@ class VariableRegistry{ static bool has(std::string key){ return vr.has(key); } - static vector getKeys(){ + static std::vector getKeys(){ return vr.getKeys(); } }; diff --git a/src/output/daa/daa_file.h b/src/output/daa/daa_file.h index 9469784ca..ab59ad893 100644 --- a/src/output/daa/daa_file.h +++ b/src/output/daa/daa_file.h @@ -62,7 +62,7 @@ struct DAA_header2 double k, double lambda, double evalue, - const string &score_matrix, + const std::string &score_matrix, unsigned mode): diamond_build (Const::build_version), db_seqs (db_seqs), @@ -101,7 +101,7 @@ struct DAA_header2 struct DAA_file { - DAA_file(const string& file_name): + DAA_file(const std::string& file_name): f_ (file_name), query_count_ (0) { @@ -115,15 +115,15 @@ struct DAA_file if(h2_.block_size[0] == 0) throw std::runtime_error("Invalid DAA file. DIAMOND run has probably not completed successfully."); - align_mode = Align_mode(h2_.mode); + align_mode = AlignMode(h2_.mode); //ref_header.sequences = h2_.db_seqs; f_.seek(sizeof(DAA_header1) + sizeof(DAA_header2) + (size_t)h2_.block_size[0]); - string s; + std::string s; ref_name_.reserve((size_t)h2_.db_seqs_used); for(uint64_t i=0;i> s; - ref_name_.push_back(new string(s)); + ref_name_.push_back(new std::string(s)); } ref_len_.resize((size_t)h2_.db_seqs_used); f_.read(ref_len_.data(), (size_t)h2_.db_seqs_used); @@ -169,7 +169,7 @@ struct DAA_file unsigned mode() const { return (unsigned)h2_.mode; } - const string& ref_name(size_t i) const + const std::string& ref_name(size_t i) const { return ref_name_[i]; } uint32_t ref_len(size_t i) const @@ -194,7 +194,7 @@ struct DAA_file return h2_.block_size[i]; } - const vector& ref_len() const { + const std::vector& ref_len() const { return ref_len_; } @@ -221,8 +221,8 @@ struct DAA_file size_t query_count_; DAA_header1 h1_; DAA_header2 h2_; - PtrVector ref_name_; - vector ref_len_; + PtrVector ref_name_; + std::vector ref_len_; friend void write_file(DAA_file&, OutputFile&); diff --git a/src/output/daa/daa_record.cpp b/src/output/daa/daa_record.cpp index 78c90c007..5acc3c0b6 100644 --- a/src/output/daa/daa_record.cpp +++ b/src/output/daa/daa_record.cpp @@ -21,7 +21,7 @@ along with this program. If not, see . #include "../output.h" DAA_format::DAA_format() : - Output_format(daa, HspValues::TRANSCRIPT, config.salltitles ? Output::Flags::FULL_TITLES : (config.sallseqid ? Output::Flags::ALL_SEQIDS : Output::Flags::NONE)) + OutputFormat(daa, HspValues::TRANSCRIPT, config.salltitles ? Output::Flags::FULL_TITLES : (config.sallseqid ? Output::Flags::ALL_SEQIDS : Output::Flags::NONE)) {} BinaryBuffer::Iterator DAA_query_record::init(const BinaryBuffer &buf) @@ -32,7 +32,7 @@ BinaryBuffer::Iterator DAA_query_record::init(const BinaryBuffer &buf) it >> query_name; uint8_t flags; it >> flags; - if (file_.mode() == Align_mode::blastp) { + if (file_.mode() == AlignMode::blastp) { Packed_sequence seq(it, query_len, false, 5); seq.unpack(context[0], 5, query_len); query_seq = TranslatedSequence(Sequence(context[0])); @@ -47,38 +47,38 @@ BinaryBuffer::Iterator DAA_query_record::init(const BinaryBuffer &buf) return it; } -BinaryBuffer::Iterator& operator>>(BinaryBuffer::Iterator &it, DAA_query_record::Match &r) +void DAA_query_record::Match::read(BinaryBuffer::Iterator &it) { - const uint32_t old_subject = r.subject_id; - it >> r.subject_id; - if (r.subject_id == old_subject) - ++r.hsp_num; + const uint32_t old_subject = subject_id; + it >> subject_id; + if (subject_id == old_subject) + ++hsp_num; else { - r.hsp_num = 0; - ++r.hit_num; + hsp_num = 0; + ++hit_num; } uint8_t flag; it >> flag; - it.read_packed(flag & 3, r.score); + it.read_packed(flag & 3, score); uint32_t query_begin, subject_begin; it.read_packed((flag >> 2) & 3, query_begin); it.read_packed((flag >> 4) & 3, subject_begin); - r.subject_range.begin_ = (int)subject_begin; - r.transcript.read(it); - r.subject_name = r.parent_.file_.ref_name(r.subject_id); - r.subject_len = r.parent_.file_.ref_len(r.subject_id); - if (r.parent_.file_.mode() == Align_mode::blastx) { - r.frame = (flag&(1 << 6)) == 0 ? query_begin % 3 : 3 + (r.parent_.source_seq.size() - 1 - query_begin) % 3; - r.set_translated_query_begin(query_begin, (unsigned)r.parent_.source_seq.size()); + subject_range.begin_ = (int)subject_begin; + transcript.read(it); + subject_name = parent_.file_.ref_name(subject_id); + subject_len = parent_.file_.ref_len(subject_id); + if (parent_.file_.mode() == AlignMode::blastx) { + frame = (flag&(1 << 6)) == 0 ? query_begin % 3 : 3 + (parent_.source_seq.size() - 1 - query_begin) % 3; + set_translated_query_begin(query_begin, (unsigned)parent_.source_seq.size()); } - else if (r.parent_.file_.mode() == Align_mode::blastp) { - r.frame = 0; - r.query_range.begin_ = query_begin; + else if (parent_.file_.mode() == AlignMode::blastp) { + frame = 0; + query_range.begin_ = query_begin; } - r.context().parse(); - r.evalue = score_matrix.evalue(r.score, r.parent_.context[0].size(), r.subject_len); - r.bit_score = score_matrix.bitscore(r.score); - return it; + + context().parse(nullptr); + evalue = score_matrix.evalue(score, (Loc)parent_.context[0].size(), subject_len); + bit_score = score_matrix.bitscore(score); } void copy_match_record_raw(BinaryBuffer::Iterator& it, TextBuffer& buf, const std::unordered_map& subject_map) { @@ -101,4 +101,4 @@ void copy_match_record_raw(BinaryBuffer::Iterator& it, TextBuffer& buf, const st it >> c; buf << c; } while (c != t); -} \ No newline at end of file +} diff --git a/src/output/daa/daa_record.h b/src/output/daa/daa_record.h index 571e7db56..de7915372 100644 --- a/src/output/daa/daa_record.h +++ b/src/output/daa/daa_record.h @@ -22,7 +22,7 @@ along with this program. If not, see . #include "daa_file.h" #include "../basic/packed_sequence.h" #include "../basic/value.h" -#include "../basic/translate.h" +#include "../util/sequence/translate.h" #include "../basic/packed_transcript.h" #include "../stats/score_matrix.h" #include "../basic/match.h" @@ -49,6 +49,7 @@ struct DAA_query_record { return HspContext(*this, (unsigned)parent_.query_num, + 0, parent_.query_seq, parent_.query_name.c_str(), subject_id, @@ -59,13 +60,14 @@ struct DAA_query_record Sequence()); } + void read(BinaryBuffer::Iterator &it); + uint32_t hsp_num, hit_num, subject_id, subject_len; - string subject_name; + std::string subject_name; private: const DAA_query_record &parent_; - friend BinaryBuffer::Iterator& operator>>(BinaryBuffer::Iterator &it, Match &r); }; @@ -92,7 +94,7 @@ struct DAA_query_record } Match_iterator& operator++() { - if (it_.good()) it_ >> r_; else good_ = false; return *this; + if (it_.good()) r_.read(it_); else good_ = false; return *this; } private: Match r_; @@ -133,9 +135,7 @@ struct DAA_query_record const DAA_file& file_; const BinaryBuffer::Iterator it_; - friend BinaryBuffer::Iterator& operator>>(BinaryBuffer::Iterator &it, Match &r); - }; -BinaryBuffer::Iterator& operator>>(BinaryBuffer::Iterator &it, DAA_query_record::Match &r); + void copy_match_record_raw(BinaryBuffer::Iterator& it, TextBuffer& buf, const std::unordered_map& subject_map); \ No newline at end of file diff --git a/src/output/daa/daa_write.cpp b/src/output/daa/daa_write.cpp index 10310d644..315a9cc82 100644 --- a/src/output/daa/daa_write.cpp +++ b/src/output/daa/daa_write.cpp @@ -3,6 +3,9 @@ #include "../util/sequence/sequence.h" #include "../../basic/statistics.h" +using std::string; +using std::vector; + void init_daa(OutputFile& f) { DAA_header1 h1; diff --git a/src/output/daa/merge.cpp b/src/output/daa/merge.cpp index db2de00bd..f7fde6fb4 100644 --- a/src/output/daa/merge.cpp +++ b/src/output/daa/merge.cpp @@ -26,12 +26,13 @@ along with this program. If not, see . using std::unordered_map; using std::string; using std::endl; +using std::vector; unordered_map build_mapping(unordered_map& acc2oid, StringSet& seq_ids, vector& seq_lens, DAA_file& f) { task_timer timer(("Reading targets for file " + f.file().file_name).c_str()); unordered_map r; for (uint32_t i = 0; i < f.db_seqs_used(); ++i) { - auto it = acc2oid.emplace(f.ref_name(i), acc2oid.size()); + auto it = acc2oid.emplace(f.ref_name(i), (uint32_t)acc2oid.size()); r.emplace(i, it.first->second); if (it.second) { seq_ids.push_back(f.ref_name(i).begin(), f.ref_name(i).end()); @@ -65,7 +66,7 @@ static int64_t write_file(DAA_file& f, OutputFile& out, const unordered_map files; - const int n = config.input_ref_file.size(); + const int n = (int)config.input_ref_file.size(); unordered_map acc2oid; vector> oid_maps; StringSet seq_ids; diff --git a/src/output/daa/view.cpp b/src/output/daa/view.cpp index e908caef4..f52532ae9 100644 --- a/src/output/daa/view.cpp +++ b/src/output/daa/view.cpp @@ -28,7 +28,7 @@ along with this program. If not, see . #include "daa_file.h" #include "../util/binary_buffer.h" #include "../output_format.h" -#include "../util/task_queue.h" +#include "../legacy/util/task_queue.h" #include "../stats/score_matrix.h" #include "../data/taxonomy.h" #include "daa_write.h" @@ -37,6 +37,7 @@ along with this program. If not, see . using std::thread; using std::unique_ptr; using std::endl; +using std::vector; const unsigned view_buf_size = 32; @@ -81,35 +82,36 @@ struct View_fetcher DAA_file &daa; }; -void view_query(DAA_query_record &r, TextBuffer &out, Output_format &format, const Search::Config& cfg) +void view_query(DAA_query_record &r, TextBuffer &out, OutputFormat &format, const Search::Config& cfg) { - unique_ptr f(format.clone()); + unique_ptr f(format.clone()); size_t seek_pos; - if (format == Output_format::daa) + Output::Info info{ SeqInfo { (BlockId)r.query_num, (OId)r.query_num, r.query_name.c_str(), "", (Loc)r.query_len(), r.query_seq.source(), Sequence()}, false, nullptr, out, {} }; + if (format == OutputFormat::daa) seek_pos = write_daa_query_record(out, r.query_name.c_str(), r.query_seq.source()); else - f->print_query_intro(r.query_num, r.query_name.c_str(), (unsigned)r.query_len(), out, false, cfg); + f->print_query_intro(info); DAA_query_record::Match_iterator i = r.begin(); const unsigned top_score = i.good() ? i->score : 0; for (; i.good(); ++i) { if (i->frame > 2 && config.forwardonly) continue; - if (!config.output_range(i->hit_num, i->score, top_score)) + if (!config.output_range(i->hit_num, i->score, top_score, cfg.max_target_seqs)) break; - if (format == Output_format::daa) + if (format == OutputFormat::daa) write_daa_record(out, *i, i->subject_id); else - f->print_match(i->context(), cfg, out); + f->print_match(i->context(), info); } - if (format == Output_format::daa) + if (format == OutputFormat::daa) finish_daa_query_record(out, seek_pos); else - f->print_query_epilog(out, r.query_name.c_str(), false, cfg); + f->print_query_epilog(info); } -void view_worker(DAA_file *daa, View_writer *writer, Task_queue *queue, Output_format *format, Search::Config* cfg) +void view_worker(DAA_file *daa, View_writer *writer, TaskQueue *queue, OutputFormat *format, Search::Config* cfg) { try { @@ -147,12 +149,12 @@ void view_daa() cfg.db_seqs = daa.db_seqs(); cfg.db_letters = daa.db_letters(); - init_output(); + init_output(cfg.max_target_seqs); taxonomy.init(); timer.go("Generating output"); View_writer writer; - if (*output_format == Output_format::daa) + if (*cfg.output_format == OutputFormat::daa) init_daa(*writer.f_); BinaryBuffer buf; @@ -160,26 +162,26 @@ void view_daa() if (daa.read_query_buffer(buf, query_num)) { DAA_query_record r(daa, buf, query_num); TextBuffer out; - view_query(r, out, *output_format, cfg); + view_query(r, out, *cfg.output_format, cfg); - output_format->print_header(*writer.f_, daa.mode(), daa.score_matrix(), daa.gap_open_penalty(), daa.gap_extension_penalty(), daa.evalue(), r.query_name.c_str(), (unsigned)r.query_len()); + cfg.output_format->print_header(*writer.f_, daa.mode(), daa.score_matrix(), daa.gap_open_penalty(), daa.gap_extension_penalty(), daa.evalue(), r.query_name.c_str(), (unsigned)r.query_len()); writer(out); vector threads; - Task_queue queue(3 * config.threads_, writer); - for (size_t i = 0; i < config.threads_; ++i) - threads.emplace_back(view_worker, &daa, &writer, &queue, output_format.get(), &cfg); + TaskQueue queue(3 * config.threads_, writer); + for (int i = 0; i < config.threads_; ++i) + threads.emplace_back(view_worker, &daa, &writer, &queue, cfg.output_format.get(), &cfg); for (auto &t : threads) t.join(); } else { TextBuffer out; - output_format->print_header(*writer.f_, daa.mode(), daa.score_matrix(), daa.gap_open_penalty(), daa.gap_extension_penalty(), daa.evalue(), "", 0); + cfg.output_format->print_header(*writer.f_, daa.mode(), daa.score_matrix(), daa.gap_open_penalty(), daa.gap_extension_penalty(), daa.evalue(), "", 0); writer(out); } - if (*output_format == Output_format::daa) + if (*cfg.output_format == OutputFormat::daa) finish_daa(*writer.f_, daa); else - output_format->print_footer(*writer.f_); + cfg.output_format->print_footer(*writer.f_); } diff --git a/src/data/load_seqs.h b/src/output/def.h similarity index 59% rename from src/data/load_seqs.h rename to src/output/def.h index 66dc0fd7a..78474d4d3 100644 --- a/src/data/load_seqs.h +++ b/src/output/def.h @@ -1,9 +1,7 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2020 Max Planck Society for the Advancement of Science e.V. - Benjamin Buchfink - Eberhard Karls Universitaet Tuebingen - +Copyright (C) 2021 Max Planck Society for the Advancement of Science e.V. + Code developed by Benjamin Buchfink This program is free software: you can redistribute it and/or modify @@ -19,3 +17,34 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ + +#pragma once +#include "../util/enum.h" + +namespace Extension { + +struct Stats { + Stats() : + extension_count(0) + {} + void operator+=(const Stats& x) { + extension_count += x.extension_count; + } + int64_t extension_count; +}; + +} + +namespace Output { + +enum class Flags : int { + NONE = 0, + FULL_TITLES = 1, + ALL_SEQIDS = 1 << 1, + TARGET_SEQS = 1 << 2, + SELF_ALN_SCORES = 1 << 3 +}; + +DEFINE_ENUM_FLAG_OPERATORS(Flags) + +} diff --git a/src/output/join_blocks.cpp b/src/output/join_blocks.cpp index 9f787c3e4..7e77d5468 100644 --- a/src/output/join_blocks.cpp +++ b/src/output/join_blocks.cpp @@ -31,11 +31,13 @@ along with this program. If not, see . #include "target_culling.h" #include "../util/log_stream.h" #include "../align/global_ranking/global_ranking.h" -#include "../util/task_queue.h" +#include "../legacy/util/task_queue.h" using std::thread; using std::unique_ptr; using std::set; +using std::string; +using std::vector; struct JoinFetcher { @@ -140,26 +142,26 @@ struct JoinRecord return info_.target_oid < rhs.info_.target_oid; } - JoinRecord(unsigned ref_block, unsigned subject, BinaryBuffer::Iterator &it, const SequenceFile& db): + JoinRecord(int64_t ref_block, DictId subject, BinaryBuffer::Iterator &it, const SequenceFile& db, const OutputFormat* output_format): block_(ref_block) { - info_.read(it); + info_.read(it, output_format); same_subject_ = info_.target_dict_id == subject; - if (*output_format != Output_format::daa) + if (*output_format != OutputFormat::daa) info_.target_oid = db.oid(info_.target_dict_id, ref_block); } - static bool push_next(unsigned block, unsigned subject, BinaryBuffer::Iterator &it, vector &v, const SequenceFile& db) + static bool push_next(int64_t block, DictId subject, BinaryBuffer::Iterator &it, vector &v, const SequenceFile& db, const OutputFormat* output_format) { if (it.good()) { - v.push_back(JoinRecord(block, subject, it, db)); + v.push_back(JoinRecord(block, subject, it, db, output_format)); return true; } else return false; } - unsigned block_; + int64_t block_; bool same_subject_; IntermediateRecord info_; @@ -167,24 +169,24 @@ struct JoinRecord struct BlockJoiner { - BlockJoiner(vector &buf, const SequenceFile& db) + BlockJoiner(vector &buf, const SequenceFile& db, const OutputFormat* output_format) { - for (size_t i = 0; i < buf.size(); ++i) { - it.push_back(buf[i].begin()); - JoinRecord::push_next(i, std::numeric_limits::max(), it.back(), records, db); + for (auto i = buf.begin(); i != buf.end(); ++i) { + it.push_back(i->begin()); + JoinRecord::push_next(i - buf.begin(), std::numeric_limits::max(), it.back(), records, db, output_format); } //std::make_heap(records.begin(), records.end(), (config.toppercent == 100.0 && config.global_ranking_targets == 0) ? JoinRecord::cmp_evalue : JoinRecord::cmp_score); std::make_heap(records.begin(), records.end(), (config.toppercent == 100.0) ? JoinRecord::cmp_evalue : JoinRecord::cmp_score); } - bool get(vector &target_hsp, unsigned & block_idx, size_t& target_oid, const SequenceFile& db) + bool get(vector &target_hsp, int64_t& block_idx, OId& target_oid, const SequenceFile& db, const OutputFormat* output_format) { if (records.empty()) return false; const JoinRecord &first = records.front(); - const unsigned block = first.block_; + const int64_t block = first.block_; block_idx = block; target_oid = first.info_.target_oid; - const unsigned subject = first.info_.target_dict_id; + const DictId subject = first.info_.target_dict_id; target_hsp.clear(); //const auto pred = (config.toppercent == 100.0 && config.global_ranking_targets == 0) ? JoinRecord::cmp_evalue : JoinRecord::cmp_score; const auto pred = (config.toppercent == 100.0) ? JoinRecord::cmp_evalue : JoinRecord::cmp_score; @@ -195,7 +197,7 @@ struct BlockJoiner target_hsp.push_back(next.info_); std::pop_heap(records.begin(), records.end(), pred); records.pop_back(); - if (JoinRecord::push_next(block, subject, it[block], records, db)) + if (JoinRecord::push_next(block, subject, it[block], records, db, output_format)) std::push_heap(records.begin(), records.end(), pred); } while (!records.empty()); return true; @@ -211,24 +213,25 @@ void join_query( unsigned query, const char *query_name, unsigned query_source_len, - Output_format &f, + OutputFormat &f, const Search::Config &cfg) { TranslatedSequence query_seq(cfg.query->translated(query)); - const double query_self_aln_score = flag_any(output_format->flags, Output::Flags::SELF_ALN_SCORES) ? cfg.query->self_aln_score(query) : 0.0; - BlockJoiner joiner(buf, *cfg.db); + Output::Info info = { cfg.query->seq_info(query), false, cfg.db.get(), out, {} }; + const double query_self_aln_score = flag_any(cfg.output_format->flags, Output::Flags::SELF_ALN_SCORES) ? cfg.query->self_aln_score(query) : 0.0; + BlockJoiner joiner(buf, *cfg.db, cfg.output_format.get()); vector target_hsp; - unique_ptr culling(TargetCulling::get()); + unique_ptr culling(TargetCulling::get(cfg.max_target_seqs)); unsigned n_target_seq = 0; - unsigned block_idx = 0; - size_t target_oid; + int64_t block_idx = 0; + OId target_oid; - while (joiner.get(target_hsp, block_idx, target_oid, *cfg.db)) { - const size_t dict_id = target_hsp.front().target_dict_id; - const set rank_taxon_ids = config.taxon_k ? cfg.taxon_nodes->rank_taxid(cfg.db->taxids(target_oid), Rank::species) : set(); + while (joiner.get(target_hsp, block_idx, target_oid, *cfg.db, cfg.output_format.get())) { + const DictId dict_id = target_hsp.front().target_dict_id; + const set rank_taxon_ids = config.taxon_k ? cfg.db->taxon_nodes().rank_taxid(cfg.db->taxids(target_oid), Rank::species) : set(); const int c = culling->cull(target_hsp, rank_taxon_ids); - const double target_self_aln_score = flag_any(output_format->flags, Output::Flags::SELF_ALN_SCORES) ? cfg.db->dict_self_aln_score(dict_id, block_idx) : 0.0; + const double target_self_aln_score = flag_any(cfg.output_format->flags, Output::Flags::SELF_ALN_SCORES) ? cfg.db->dict_self_aln_score(dict_id, block_idx) : 0.0; if (c == TargetCulling::FINISHED) break; else if (c == TargetCulling::NEXT) @@ -236,25 +239,28 @@ void join_query( unsigned hsp_num = 0; for (vector::const_iterator i = target_hsp.begin(); i != target_hsp.end(); ++i, ++hsp_num) { - if (f == Output_format::daa) + if (f == OutputFormat::daa) write_daa_record(out, *i); /*else if (config.global_ranking_targets > 0) Extension::GlobalRanking::write_merged_query_list(*i, out, ranking_db_filter, statistics);*/ else { - Hsp hsp(*i, query_source_len); + const Loc tlen = cfg.db->dict_len(dict_id, block_idx); + const unsigned frame = i->frame(query_source_len, align_mode.mode); + Hsp hsp(*i, query_source_len, query_seq.index(frame).length(), tlen, cfg.output_format.get()); f.print_match(HspContext(hsp, query, + cfg.query->block_id2oid(query), query_seq, query_name, target_oid, - cfg.db->dict_len(dict_id, block_idx), + tlen, cfg.db->dict_title(dict_id, block_idx).c_str(), n_target_seq, hsp_num, flag_any(f.flags, Output::Flags::TARGET_SEQS) ? Sequence(cfg.db->dict_seq(dict_id, block_idx)) : Sequence(), 0, query_self_aln_score, - target_self_aln_score).parse(), cfg, out); + target_self_aln_score).parse(cfg.output_format.get()), info); } } @@ -267,7 +273,7 @@ void join_query( } } -void join_worker(Task_queue *queue, const Search::Config* cfg, BitVector* ranking_db_filter_out) +void join_worker(TaskQueue *queue, const Search::Config* cfg, BitVector* ranking_db_filter_out) { try { static std::mutex mtx; @@ -287,30 +293,32 @@ void join_worker(Task_queue *queue, const Search::Config const Sequence query_seq = align_mode.query_translated ? cfg->query->source_seqs()[fetcher.query_id] : cfg->query->seqs()[fetcher.query_id]; - if (*output_format != Output_format::daa && config.report_unaligned != 0) { + if (*cfg->output_format != OutputFormat::daa && config.report_unaligned != 0) { for (unsigned i = fetcher.unaligned_from; i < fetcher.query_id; ++i) { - output_format->print_query_intro(i, qids[i], cfg->query->source_len(i), *out, true, *cfg); - output_format->print_query_epilog(*out, qids[i], true, *cfg); + Output::Info info{ cfg->query->seq_info(i), true, cfg->db.get(), *out, {} }; + cfg->output_format->print_query_intro(info); + cfg->output_format->print_query_epilog(info); } } - unique_ptr f(output_format->clone()); + unique_ptr f(cfg->output_format->clone()); - if (*f == Output_format::daa) + Output::Info info{ cfg->query->seq_info(fetcher.query_id), false, cfg->db.get(), *out, {} }; + if (*f == OutputFormat::daa) seek_pos = write_daa_query_record(*out, query_name, query_seq); /*else if (config.global_ranking_targets) seek_pos = Extension::GlobalRanking::write_merged_query_list_intro(fetcher.query_id, *out);*/ else - f->print_query_intro(fetcher.query_id, query_name, (unsigned)query_seq.length(), *out, false, *cfg); + f->print_query_intro(info); join_query(fetcher.buf, *out, stat, fetcher.query_id, query_name, (unsigned)query_seq.length(), *f, *cfg); // ranking_db_filter); - if (*f == Output_format::daa) + if (*f == OutputFormat::daa) finish_daa_query_record(*out, seek_pos); /*else if (config.global_ranking_targets) Extension::GlobalRanking::finish_merged_query_list(*out, seek_pos);*/ else - f->print_query_epilog(*out, query_name, false, *cfg); + f->print_query_epilog(info); queue->push(n); } @@ -325,11 +333,11 @@ void join_worker(Task_queue *queue, const Search::Config } } -void join_blocks(unsigned ref_blocks, Consumer &master_out, const PtrVector &tmp_file, Search::Config& cfg, SequenceFile &db_file, +void join_blocks(int64_t ref_blocks, Consumer &master_out, const PtrVector &tmp_file, Search::Config& cfg, SequenceFile &db_file, const vector tmp_file_names) { - if (*output_format != Output_format::daa) - cfg.db->init_random_access(current_query_chunk, config.multiprocessing ? tmp_file_names.size() : tmp_file.size()); + if (*cfg.output_format != OutputFormat::daa) + cfg.db->init_random_access(cfg.current_query_block, config.multiprocessing ? tmp_file_names.size() : tmp_file.size()); task_timer timer("Joining output blocks"); if (tmp_file_names.size() > 0) { @@ -345,25 +353,26 @@ void join_blocks(unsigned ref_blocks, Consumer &master_out, const PtrVector queue(3 * config.threads_, writer); + TaskQueue queue(3 * config.threads_, writer); vector threads; //BitVector ranking_db_filter(config.global_ranking_targets > 0 ? cfg.db_seqs : 0); - for (unsigned i = 0; i < config.threads_; ++i) + for (int i = 0; i < config.threads_; ++i) threads.emplace_back(join_worker, &queue, &cfg, nullptr); // &ranking_db_filter); for (auto &t : threads) t.join(); JoinFetcher::finish(); - if (*output_format != Output_format::daa && config.report_unaligned != 0) { + if (*cfg.output_format != OutputFormat::daa && config.report_unaligned != 0) { TextBuffer out; - for (unsigned i = JoinFetcher::query_last + 1; i < query_ids.size(); ++i) { - output_format->print_query_intro(i, query_ids[i], cfg.query->source_len(i), out, true, cfg); - output_format->print_query_epilog(out, query_ids[i], true, cfg); + for (BlockId i = JoinFetcher::query_last + 1; i < query_ids.size(); ++i) { + Output::Info info{ cfg.query->seq_info(i), true, cfg.db.get(), out, {} }; + cfg.output_format->print_query_intro(info); + cfg.output_format->print_query_epilog(info); } writer(out); } /*if (config.global_ranking_targets) Extension::GlobalRanking::extend(db_file, *merged_query_list, ranking_db_filter, cfg, master_out);*/ - if (*output_format != Output_format::daa) + if (*cfg.output_format != OutputFormat::daa) cfg.db->end_random_access(); } \ No newline at end of file diff --git a/src/output/output.h b/src/output/output.h index dc5094c95..f14f64cfa 100644 --- a/src/output/output.h +++ b/src/output/output.h @@ -33,6 +33,7 @@ along with this program. If not, see . #include "../util/io/consumer.h" #include "output_format.h" #include "../run/config.h" +#include "../util/data_structures/reorder_queue.h" inline unsigned get_length_flag(unsigned x) { @@ -68,62 +69,37 @@ inline uint8_t get_segment_flag(const HspContext &match) struct IntermediateRecord { - void read(BinaryBuffer::Iterator& f); - interval absolute_query_range() const; + void read(BinaryBuffer::Iterator& f, const OutputFormat* output_format); + unsigned frame(Loc query_source_len, int align_mode) const; + Interval absolute_query_range() const; static size_t write_query_intro(TextBuffer& buf, unsigned query_id); static void finish_query(TextBuffer& buf, size_t seek_pos); - static void write(TextBuffer& buf, const Hsp& match, unsigned query_id, size_t target_dict_id, size_t target_oid); + static void write(TextBuffer& buf, const Hsp& match, unsigned query_id, DictId target, size_t target_oid, const OutputFormat* output_format); static void write(TextBuffer& buf, uint32_t target_block_id, int score, const Search::Config& cfg); static void finish_file(Consumer& f); static bool stats_mode(const HspValues v) { return !flag_any(v, HspValues::TRANSCRIPT) && v != HspValues::NONE; } - static const uint32_t FINISHED = UINT32_MAX; - uint32_t score, query_id, target_dict_id, target_oid, query_begin, subject_begin, query_end, subject_end, identities, mismatches, positives, length, gap_openings, gaps; + static const uint32_t FINISHED; + BlockId query_id; + DictId target_dict_id; + OId target_oid; + uint32_t score, query_begin, subject_begin, query_end, subject_end, identities, mismatches, positives, length, gap_openings, gaps; double evalue; uint8_t flag; Packed_transcript transcript; }; -void join_blocks(unsigned ref_blocks, Consumer &master_out, const PtrVector &tmp_file, Search::Config& cfg, SequenceFile &db_file, - const vector tmp_file_names = vector()); +void join_blocks(int64_t ref_blocks, Consumer &master_out, const PtrVector &tmp_file, Search::Config& cfg, SequenceFile &db_file, + const std::vector tmp_file_names = std::vector()); -struct OutputSink -{ - OutputSink(size_t begin, Consumer *f) : - f_(f), - begin_(begin), - next_(begin), - size_(0), - max_size_(0) - {} - void push(size_t n, TextBuffer *buf); - size_t size() const - { - return size_; - } - size_t max_size() const - { - return max_size_; - } - static OutputSink& get() - { - return *instance; +struct OutputWriter { + void operator()(TextBuffer* buf) { + file_->consume(buf->data(), buf->size()); } - size_t next() const - { - return next_; - } - size_t begin() const { - return begin_; - } - static std::unique_ptr instance; -private: - void flush(TextBuffer *buf); - std::mutex mtx_; - Consumer* const f_; - std::map backlog_; - size_t begin_, next_, size_, max_size_; + Consumer* file_; }; +extern std::unique_ptr> output_sink; + void heartbeat_worker(size_t qend, const Search::Config* cfg); diff --git a/src/output/output_format.cpp b/src/output/output_format.cpp index 9053e48ff..2679af897 100644 --- a/src/output/output_format.cpp +++ b/src/output/output_format.cpp @@ -1,6 +1,10 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2018 Benjamin Buchfink +Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. + Benjamin Buchfink + Eberhard Karls Universitaet Tuebingen + +Code developed by Benjamin Buchfink This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,11 +33,14 @@ along with this program. If not, see . #include "../run/config.h" #include "../util/sequence/sequence.h" -using namespace std; +using std::endl; +using std::runtime_error; +using std::string; +using std::vector; -unique_ptr output_format; +const uint32_t IntermediateRecord::FINISHED = UINT32_MAX; -void IntermediateRecord::read(BinaryBuffer::Iterator& f) +void IntermediateRecord::read(BinaryBuffer::Iterator& f, const OutputFormat* output_format) { f.read(target_dict_id); /*if (config.global_ranking_targets > 0) { @@ -42,7 +49,7 @@ void IntermediateRecord::read(BinaryBuffer::Iterator& f) score = s; return; }*/ - if (*output_format == Output_format::daa) + if (*output_format == OutputFormat::daa) f.read(target_oid); f.read(flag); f.read_packed(flag & 3, score); @@ -68,12 +75,19 @@ void IntermediateRecord::read(BinaryBuffer::Iterator& f) } } -interval IntermediateRecord::absolute_query_range() const +unsigned IntermediateRecord::frame(Loc query_source_len, int align_mode) const { + if (align_mode == AlignMode::blastx) + return (flag&(1 << 6)) == 0 ? query_begin % 3 : 3 + (query_source_len - 1 - query_begin) % 3; + else + return 0; +} + +Interval IntermediateRecord::absolute_query_range() const { if (query_begin < query_end) - return interval(query_begin, query_end + 1); + return Interval(query_begin, query_end + 1); else - return interval(query_end, query_begin + 1); + return Interval(query_end, query_begin + 1); } size_t IntermediateRecord::write_query_intro(TextBuffer& buf, unsigned query_id) @@ -88,11 +102,11 @@ void IntermediateRecord::finish_query(TextBuffer& buf, size_t seek_pos) *(uint32_t*)(&buf[seek_pos + sizeof(uint32_t)]) = safe_cast(buf.size() - seek_pos - sizeof(uint32_t) * 2); } -void IntermediateRecord::write(TextBuffer& buf, const Hsp& match, unsigned query_id, size_t target_dict_id, size_t target_oid) +void IntermediateRecord::write(TextBuffer& buf, const Hsp& match, unsigned query_id, DictId target, size_t target_oid, const OutputFormat* output_format) { - const interval oriented_range(match.oriented_range()); - buf.write((uint32_t)target_dict_id); - if (*output_format == Output_format::daa) + const Interval oriented_range(match.oriented_range()); + buf.write(target); + if (*output_format == OutputFormat::daa) buf.write((uint32_t)target_oid); buf.write(get_segment_flag(match)); buf.write_packed(match.score); @@ -131,7 +145,7 @@ void IntermediateRecord::finish_file(Consumer& f) f.consume(reinterpret_cast(&i), 4); } -void Output_format::print_title(TextBuffer &buf, const char *id, bool full_titles, bool all_titles, const char *separator, const EscapeSequences *esc) +void OutputFormat::print_title(TextBuffer &buf, const char *id, bool full_titles, bool all_titles, const char *separator, const EscapeSequences *esc) { if (!all_titles) { if (config.short_seqids) @@ -167,10 +181,10 @@ void print_hsp(Hsp &hsp, const TranslatedSequence &query) TextBuffer buf; //Pairwise_format().print_match(Hsp_context(hsp, 0, query, "", 0, 0, 0, 0, Sequence()), Search::Config(true), buf); buf << '\0'; - cout << buf.data() << endl; + std::cout << buf.data() << endl; } -Output_format* get_output_format() +OutputFormat* get_output_format() { const vector &f = config.output_format; if (f.size() == 0) { @@ -199,55 +213,61 @@ Output_format* get_output_format() return new Bin1_format; else if (f[0] == "clus") return new Clustering_format(&f[1]); - else if (f[0] == "bin") - return new Binary_format; + else if (f[0] == "edge") + return new Output::Format::Edge; else throw std::runtime_error("Invalid output format: " + f[0] + "\nAllowed values: 0,5,xml,6,tab,100,daa,101,sam,102,103,paf"); } -void init_output() +OutputFormat* init_output(const int64_t max_target_seqs) { - output_format = unique_ptr(get_output_format()); + OutputFormat* output_format = get_output_format(); if(config.command == Config::view && (output_format->needs_taxon_id_lists || output_format->needs_taxon_nodes || output_format->needs_taxon_scientific_names)) throw runtime_error("Taxonomy features are not supported for the DAA format."); - if (*output_format == Output_format::daa && config.multiprocessing) + if (*output_format == OutputFormat::daa && config.multiprocessing) throw std::runtime_error("The DAA format is not supported in multiprocessing mode."); - if (*output_format == Output_format::daa && config.global_ranking_targets) + if (*output_format == OutputFormat::daa && config.global_ranking_targets) throw std::runtime_error("The DAA format is not supported in global ranking mode."); - if (*output_format == Output_format::taxon && config.toppercent == 100.0 && config.min_bit_score == 0.0) + if (*output_format == OutputFormat::taxon && config.toppercent == 100.0 && config.min_bit_score == 0.0) config.toppercent = 10.0; if (config.toppercent == 100.0) { - message_stream << "#Target sequences to report alignments for: "; - if (config.max_alignments == 0) { - config.max_alignments = std::numeric_limits::max(); - message_stream << "unlimited" << endl; + if (max_target_seqs >= 0) { + message_stream << "#Target sequences to report alignments for: "; + if (max_target_seqs == INT64_MAX || max_target_seqs == 0) + message_stream << "unlimited"; + else + message_stream << max_target_seqs; + message_stream << endl; } - else - message_stream << config.max_alignments << endl; } else message_stream << "Percentage range of top alignment score to report hits: " << config.toppercent << endl; if (config.frame_shift != 0 && (output_format->hsp_values != HspValues::NONE || config.query_range_culling)) output_format->hsp_values = HspValues::TRANSCRIPT; log_stream << "DP fields: " << (unsigned)output_format->hsp_values << endl; + return output_format; } -void Bin1_format::print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const { - out.write(std::numeric_limits::max()); - out.write((uint32_t)query_num); +void Bin1_format::print_query_intro(Output::Info& info) const { + info.out.write(std::numeric_limits::max()); + info.out.write((uint32_t)info.query.block_id); } -void Bin1_format::print_match(const HspContext& r, const Search::Config &metadata, TextBuffer &out) { +void Bin1_format::print_match(const HspContext& r, Output::Info& info) { if (r.query_id < r.subject_oid) { - out.write((uint32_t)r.subject_oid); - out.write(r.bit_score() / std::max(r.query.source().length(), r.subject_len)); + info.out.write((uint32_t)r.subject_oid); + info.out.write(r.bit_score() / std::max(r.query.source().length(), r.subject_len)); } } -void Binary_format::print_match(const HspContext& r, const Search::Config& metadata, TextBuffer& out) +namespace Output { namespace Format { + +void Edge::print_match(const HspContext& r, Output::Info& info) { - out.write((uint32_t)metadata.query->block_id2oid(r.query_id)); - out.write((uint32_t)r.subject_oid); + info.out.write(Data{ r.query_oid, r.subject_oid, (float)r.qcovhsp(), (float)r.scovhsp(), + config.mmseqs_compat ? (r.evalue() == 0.0 ? r.bit_score() : -r.evalue()) + : r.corrected_bit_score() }); } +}} \ No newline at end of file diff --git a/src/output/output_format.h b/src/output/output_format.h index 3112de1e8..6046a5679 100644 --- a/src/output/output_format.h +++ b/src/output/output_format.h @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -31,28 +31,26 @@ along with this program. If not, see . #include "../stats/score_matrix.h" #include "../util/escape_sequences.h" #include "../util/io/consumer.h" -#include "../output/recursive_parser.h" #include "../util/enum.h" #include "../run/config.h" #include "../dp/flags.h" +#include "def.h" namespace Output { -enum class Flags : int { - NONE = 0, - FULL_TITLES = 1, - ALL_SEQIDS = 1 << 1, - TARGET_SEQS = 1 << 2, - SELF_ALN_SCORES = 1 << 3 +struct Info { + SeqInfo query; + bool unaligned; + SequenceFile* db; + TextBuffer& out; + Extension::Stats stats; }; -DEF_ENUM_FLAG_OPERATORS(Flags) - } -struct Output_format +struct OutputFormat { - Output_format(unsigned code, HspValues hsp_values = HspValues::TRANSCRIPT, Output::Flags flags = Output::Flags::NONE): + OutputFormat(unsigned code, HspValues hsp_values = HspValues::TRANSCRIPT, Output::Flags flags = Output::Flags::NONE): code(code), needs_taxon_id_lists(false), needs_taxon_nodes(false), @@ -62,18 +60,18 @@ struct Output_format hsp_values(hsp_values), flags(flags) {} - virtual void print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const + virtual void print_query_intro(Output::Info& info) const {} - virtual void print_query_epilog(TextBuffer &out, const char *query_title, bool unaligned, const Search::Config ¶meters) const + virtual void print_query_epilog(Output::Info& info) const {} - virtual void print_match(const HspContext& r, const Search::Config &metadata, TextBuffer &out) + virtual void print_match(const HspContext& r, Output::Info& info) {} virtual void print_header(Consumer &f, int mode, const char *matrix, int gap_open, int gap_extend, double evalue, const char *first_query_name, unsigned first_query_len) const { } virtual void print_footer(Consumer &f) const { } - virtual Output_format* clone() const = 0; - virtual ~Output_format() + virtual OutputFormat* clone() const = 0; + virtual ~OutputFormat() { } static void print_title(TextBuffer &buf, const char *id, bool full_titles, bool all_titles, const char *separator, const EscapeSequences *esc = 0); operator unsigned() const @@ -84,139 +82,141 @@ struct Output_format bool needs_taxon_id_lists, needs_taxon_nodes, needs_taxon_scientific_names, needs_taxon_ranks, needs_paired_end_info; HspValues hsp_values; Output::Flags flags; - enum { daa, blast_tab, blast_xml, sam, blast_pairwise, null, taxon, paf, bin1 }; + enum { daa, blast_tab, blast_xml, sam, blast_pairwise, null, taxon, paf, bin1, EDGE }; }; -extern std::unique_ptr output_format; - -struct Null_format : public Output_format +struct Null_format : public OutputFormat { Null_format() : - Output_format(null) + OutputFormat(null) {} - virtual Output_format* clone() const + virtual OutputFormat* clone() const { return new Null_format(*this); } }; -struct DAA_format : public Output_format +struct DAA_format : public OutputFormat { DAA_format(); - virtual Output_format* clone() const + virtual OutputFormat* clone() const { return new DAA_format(*this); } }; struct OutputField { - const std::string key, description; + const std::string key, clust_key, description; const HspValues hsp_values; const Output::Flags flags; }; -struct Blast_tab_format : public Output_format +enum class Header { NONE, SIMPLE, VERBOSE }; + +struct Blast_tab_format : public OutputFormat { static const std::vector field_def; Blast_tab_format(); virtual void print_header(Consumer &f, int mode, const char *matrix, int gap_open, int gap_extend, double evalue, const char *first_query_name, unsigned first_query_len) const override; - virtual void print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const override; - virtual void print_match(const HspContext& r, const Search::Config& metadata, TextBuffer &out) override; + virtual void print_query_intro(Output::Info& info) const override; + virtual void print_match(const HspContext& r, Output::Info& info) override; virtual ~Blast_tab_format() { } - virtual Output_format* clone() const override + virtual OutputFormat* clone() const override { return new Blast_tab_format(*this); } - vector fields; + static Header header_format(unsigned workflow); + void output_header(Consumer& f, bool cluster) const; + std::vector fields; }; -struct PAF_format : public Output_format +struct PAF_format : public OutputFormat { PAF_format(): - Output_format(paf, HspValues::TRANSCRIPT, Output::Flags::NONE) + OutputFormat(paf, HspValues::TRANSCRIPT, Output::Flags::NONE) {} - virtual void print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const; + virtual void print_query_intro(Output::Info& info) const; virtual void print_match(const HspContext& r, const Search::Config& metadata, TextBuffer &out); virtual ~PAF_format() { } - virtual Output_format* clone() const + virtual OutputFormat* clone() const { return new PAF_format(*this); } }; -struct Sam_format : public Output_format +struct Sam_format : public OutputFormat { Sam_format(): - Output_format(sam, HspValues::TRANSCRIPT, Output::Flags::NONE) + OutputFormat(sam, HspValues::TRANSCRIPT, Output::Flags::NONE) { } - virtual void print_match(const HspContext& r, const Search::Config &metadata, TextBuffer &out) override; + virtual void print_match(const HspContext& r, Output::Info& info) override; virtual void print_header(Consumer &f, int mode, const char *matrix, int gap_open, int gap_extend, double evalue, const char *first_query_name, unsigned first_query_len) const override; - virtual void print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const override; + virtual void print_query_intro(Output::Info& info) const override; virtual ~Sam_format() { } - virtual Output_format* clone() const override + virtual OutputFormat* clone() const override { return new Sam_format(*this); } }; -struct XML_format : public Output_format +struct XML_format : public OutputFormat { XML_format(): - Output_format(blast_xml, HspValues::TRANSCRIPT, Output::Flags::FULL_TITLES) + OutputFormat(blast_xml, HspValues::TRANSCRIPT, Output::Flags::FULL_TITLES) { config.salltitles = true; } - virtual void print_match(const HspContext& r, const Search::Config &metadata, TextBuffer &out) override; + virtual void print_match(const HspContext& r, Output::Info& info) override; virtual void print_header(Consumer &f, int mode, const char *matrix, int gap_open, int gap_extend, double evalue, const char *first_query_name, unsigned first_query_len) const override; - virtual void print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const override; - virtual void print_query_epilog(TextBuffer &out, const char *query_title, bool unaligned, const Search::Config ¶meters) const override; + virtual void print_query_intro(Output::Info& info) const override; + virtual void print_query_epilog(Output::Info& info) const override; virtual void print_footer(Consumer &f) const override; virtual ~XML_format() { } - virtual Output_format* clone() const override + virtual OutputFormat* clone() const override { return new XML_format(*this); } }; -struct Pairwise_format : public Output_format +struct Pairwise_format : public OutputFormat { Pairwise_format() : - Output_format(blast_pairwise, HspValues::TRANSCRIPT, Output::Flags::FULL_TITLES) + OutputFormat(blast_pairwise, HspValues::TRANSCRIPT, Output::Flags::FULL_TITLES) { config.salltitles = true; } - virtual void print_match(const HspContext& r, const Search::Config &metadata, TextBuffer &out) override; + virtual void print_match(const HspContext& r, Output::Info& info) override; virtual void print_header(Consumer &f, int mode, const char *matrix, int gap_open, int gap_extend, double evalue, const char *first_query_name, unsigned first_query_len) const override; - virtual void print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const override; - virtual void print_query_epilog(TextBuffer &out, const char *query_title, bool unaligned, const Search::Config ¶meters) const override; + virtual void print_query_intro(Output::Info& info) const override; + virtual void print_query_epilog(Output::Info& infos) const override; virtual void print_footer(Consumer &f) const override; virtual ~Pairwise_format() { } - virtual Output_format* clone() const override + virtual OutputFormat* clone() const override { return new Pairwise_format(*this); } }; -struct Taxon_format : public Output_format +struct Taxon_format : public OutputFormat { Taxon_format() : - Output_format(taxon, HspValues::NONE, Output::Flags::NONE), + OutputFormat(taxon, HspValues::NONE, Output::Flags::NONE), taxid(0), evalue(std::numeric_limits::max()) { needs_taxon_id_lists = true; needs_taxon_nodes = true; } - virtual void print_match(const HspContext& r, const Search::Config &metadata, TextBuffer &out) override; - virtual void print_query_epilog(TextBuffer &out, const char *query_title, bool unaligned, const Search::Config ¶meters) const override; + virtual void print_match(const HspContext& r, Output::Info& info) override; + virtual void print_query_epilog(Output::Info& info) const override; virtual ~Taxon_format() { } - virtual Output_format* clone() const override + virtual OutputFormat* clone() const override { return new Taxon_format(*this); } @@ -224,52 +224,58 @@ struct Taxon_format : public Output_format double evalue; }; -struct Bin1_format : public Output_format +struct Bin1_format : public OutputFormat { Bin1_format(): - Output_format(bin1) + OutputFormat(bin1) {} - virtual void print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const override; - virtual void print_match(const HspContext& r, const Search::Config &metadata, TextBuffer &out) override; + virtual void print_query_intro(Output::Info& info) const override; + virtual void print_match(const HspContext& r, Output::Info& info) override; virtual ~Bin1_format() { } - virtual Output_format* clone() const override + virtual OutputFormat* clone() const override { return new Bin1_format(*this); } }; -struct Clustering_format : public Output_format +struct Clustering_format : public OutputFormat { - string format; - Clustering_format(const string* const format): Output_format(bin1) { - this->format = RecursiveParser::clean_expression(format); - } - virtual void print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const override; - virtual void print_match(const HspContext& r, const Search::Config &metadata, TextBuffer &out) override; + std::string format; + Clustering_format(const std::string* const format); + virtual void print_match(const HspContext& r, Output::Info& info) override; virtual ~Clustering_format() { } - virtual Output_format* clone() const override + virtual OutputFormat* clone() const override { return new Clustering_format(*this); } }; -struct Binary_format : public Output_format +namespace Output { namespace Format { + +struct Edge : public OutputFormat { - Binary_format() : - Output_format(bin1, HspValues::NONE) + struct Data { + OId query, target; + float qcovhsp, scovhsp; + double evalue; + }; + Edge() : + OutputFormat(EDGE, HspValues::COORDS) {} - virtual void print_match(const HspContext& r, const Search::Config & metadata, TextBuffer& out) override; - virtual ~Binary_format() + virtual void print_match(const HspContext& r, Output::Info& info) override; + virtual ~Edge() { } - virtual Output_format* clone() const override + virtual OutputFormat* clone() const override { - return new Binary_format(*this); + return new Edge(*this); } }; -Output_format* get_output_format(); -void init_output(); +}} + +OutputFormat* get_output_format(); +OutputFormat* init_output(const int64_t max_target_seqs); void print_hsp(Hsp &hsp, const TranslatedSequence &query); void print_cigar(const HspContext &r, TextBuffer &buf); diff --git a/src/output/output_sink.cpp b/src/output/output_sink.cpp index 10087d7a1..d42de5ac5 100644 --- a/src/output/output_sink.cpp +++ b/src/output/output_sink.cpp @@ -21,58 +21,17 @@ along with this program. If not, see . #include "output.h" #include "../data/queries.h" #include "../util/util.h" +#include "../util/parallel/thread_pool.h" using std::chrono::high_resolution_clock; using std::chrono::seconds; using std::chrono::milliseconds; using std::chrono::duration_cast; using std::endl; +using std::string; +using std::vector; -std::unique_ptr OutputSink::instance; - -void OutputSink::push(size_t n, TextBuffer *buf) -{ - mtx_.lock(); - //cout << "n=" << n << " next=" << next_ << endl; - if (n != next_) { - backlog_[n] = buf; - size_ += buf ? buf->alloc_size() : 0; - max_size_ = std::max(max_size_, size_); - mtx_.unlock(); - } - else - flush(buf); -} - -void OutputSink::flush(TextBuffer *buf) -{ - size_t n = next_ + 1; - vector out; - out.push_back(buf); - std::map::iterator i; - do { - while ((i = backlog_.begin()) != backlog_.end() && i->first == n) { - out.push_back(i->second); - backlog_.erase(i); - ++n; - } - mtx_.unlock(); - size_t size = 0; - for (vector::iterator j = out.begin(); j < out.end(); ++j) { - if (*j) { - f_->consume((*j)->data(), (*j)->size()); - if (*j != buf) - size += (*j)->alloc_size(); - delete *j; - } - } - out.clear(); - mtx_.lock(); - size_ -= size; - } while ((i = backlog_.begin()) != backlog_.end() && i->first == n); - next_ = n; - mtx_.unlock(); -} +std::unique_ptr> output_sink; void heartbeat_worker(size_t qend, const Search::Config* cfg) { @@ -80,14 +39,15 @@ void heartbeat_worker(size_t qend, const Search::Config* cfg) static thread_local high_resolution_clock::time_point t0 = high_resolution_clock::now(); int n = 0; size_t next; - while ((next = OutputSink::get().next()) < qend) { + while ((next = output_sink->next()) < qend) { if (n == interval) { const string title(cfg->query->ids()[next]); verbose_stream << "Queries=" << next - << " size=" << megabytes(OutputSink::get().size()) - << " max_size=" << megabytes(OutputSink::get().max_size()) + << " size=" << megabytes(output_sink->size()) + << " max_size=" << megabytes(output_sink->max_size()) << " next=" << title.substr(0, title.find(' ')) - << " ETA=" << (double)duration_cast(high_resolution_clock::now() - t0).count() / (next - OutputSink::get().begin()) * (qend - next) << "s" + << " queue=" << cfg->thread_pool->queue_len(0) << "/" << cfg->thread_pool->queue_len(1) + //<< " ETA=" << (double)duration_cast(high_resolution_clock::now() - t0).count() / (next - OutputSink::get().begin()) * (qend - next) << "s" << endl; n = 0; } diff --git a/src/output/paf_format.cpp b/src/output/paf_format.cpp index 6edef1ee1..857bfd58f 100644 --- a/src/output/paf_format.cpp +++ b/src/output/paf_format.cpp @@ -19,23 +19,23 @@ along with this program. If not, see . #include "output_format.h" #include "../util/sequence/sequence.h" -void PAF_format::print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const +void PAF_format::print_query_intro(Output::Info& info) const { - if (unaligned) { - out.write_until(query_name, Util::Seq::id_delimiters); - out << "\t4\t*\t0\t255\t*\t*\t0\t0\t*\t*\n"; + if (info.unaligned) { + info.out.write_until(info.query.title, Util::Seq::id_delimiters); + info.out << "\t4\t*\t0\t255\t*\t*\t0\t0\t*\t*\n"; } } void PAF_format::print_match(const HspContext& r, const Search::Config& cfg, TextBuffer &out) { - out.write_until(r.query_title, Util::Seq::id_delimiters); + out.write_until(r.query_title.c_str(), Util::Seq::id_delimiters); out << '\t' << r.query.source().length() << '\t' << r.query_source_range().begin_ << '\t' << r.query_source_range().end_ - 1 << '\t' << (Frame(r.frame()).strand == FORWARD ? '+' : '-') << '\t'; - print_title(out, r.target_title, false, false, "<>"); + print_title(out, r.target_title.c_str(), false, false, "<>"); out << '\t' << r.subject_len << '\t' << r.subject_range().begin_ << '\t' diff --git a/src/output/recursive_parser.h b/src/output/recursive_parser.h index 71b7dc1a7..7a1d0a8d8 100644 --- a/src/output/recursive_parser.h +++ b/src/output/recursive_parser.h @@ -23,10 +23,10 @@ along with this program. If not, see . #include #include -class RecursiveParser{ +class RecursiveParser { const HspContext* r; const char * expression_to_parse; - const bool check; + std::vector vars_; char peek() { return *expression_to_parse; @@ -73,7 +73,7 @@ class RecursiveParser{ } std::string variable() { - vector v; + std::vector v; while ( (peek() >= 'a' && peek() <= 'z') || (peek() >= 'A' && peek() <= 'Z') || peek() == '_') { v.push_back(get()); @@ -153,7 +153,8 @@ class RecursiveParser{ } else { auto v = VariableRegistry::get(variable()); - if(check){ + if(!r) { + vars_.push_back(v); return 4; } else { @@ -191,14 +192,16 @@ class RecursiveParser{ } public: - static const vector variables; - RecursiveParser(const HspContext* r, const char * c, bool check): r(r), expression_to_parse(c), check(check){} - double evaluate(){ + RecursiveParser(const HspContext* r, const char * c): r(r), expression_to_parse(c) + {} + double evaluate() { return expression(); } - - const string static clean_expression(const string* const expression){ - string cleanedExpression = std::string(*expression); + std::vector variables() const { + return vars_; + } + const std::string static clean_expression(const std::string* const expression){ + std::string cleanedExpression = std::string(*expression); cleanedExpression.erase(std::remove_if(cleanedExpression.begin(), cleanedExpression.end(), [](unsigned char c){return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\f';}), cleanedExpression.end()); return cleanedExpression; } diff --git a/src/output/sam_format.cpp b/src/output/sam_format.cpp index fafe0a8c0..0d186cf7c 100644 --- a/src/output/sam_format.cpp +++ b/src/output/sam_format.cpp @@ -78,21 +78,22 @@ void print_cigar(const HspContext &r, TextBuffer &buf) buf << n << letter[op]; } -void Sam_format::print_query_intro(size_t query_num, const char *query_name, unsigned query_len, TextBuffer &out, bool unaligned, const Search::Config& cfg) const +void Sam_format::print_query_intro(Output::Info &info) const { - if (unaligned) { - out.write_until(query_name, Util::Seq::id_delimiters); - out << "\t4\t*\t0\t255\t*\t*\t0\t0\t*\t*\n"; + if (info.unaligned) { + info.out.write_until(info.query.title, Util::Seq::id_delimiters); + info.out << "\t4\t*\t0\t255\t*\t*\t0\t0\t*\t*\n"; } } -void Sam_format::print_match(const HspContext& r, const Search::Config&metadata, TextBuffer &out) +void Sam_format::print_match(const HspContext& r, Output::Info &info) { - out.write_until(r.query_title, Util::Seq::id_delimiters); + TextBuffer& out = info.out; + out.write_until(r.query_title.c_str(), Util::Seq::id_delimiters); out << '\t' << '0' << '\t'; const bool lt = (config.salltitles || (config.command == Config::view)) ? true : false; - print_title(out, r.target_title, lt, lt, "<>"); + print_title(out, r.target_title.c_str(), lt, lt, "<>"); out << '\t' << r.subject_range().begin_ + 1 << '\t' diff --git a/src/output/target_culling.cpp b/src/output/target_culling.cpp index f1b81130f..efa9e4b32 100644 --- a/src/output/target_culling.cpp +++ b/src/output/target_culling.cpp @@ -18,10 +18,10 @@ along with this program. If not, see . #include "target_culling.h" -TargetCulling* TargetCulling::get() +TargetCulling* TargetCulling::get(const int64_t max_target_seqs) { if (config.query_range_culling) - return new RangeCulling; + return new RangeCulling(max_target_seqs); else - return new GlobalCulling(); + return new GlobalCulling(max_target_seqs); } \ No newline at end of file diff --git a/src/output/target_culling.h b/src/output/target_culling.h index 3b5b3dd85..4a0785efd 100644 --- a/src/output/target_culling.h +++ b/src/output/target_culling.h @@ -24,23 +24,24 @@ along with this program. If not, see . #include #include #include "../align/legacy/query_mapper.h" -#include "../util/interval_partition.h" +#include "../util/geo/interval_partition.h" #include "output.h" struct TargetCulling { virtual int cull(const Target &t) const = 0; - virtual int cull(const vector &target_hsp, const std::set &taxon_ids) const = 0; + virtual int cull(const std::vector &target_hsp, const std::set &taxon_ids) const = 0; virtual void add(const Target &t) = 0; - virtual void add(const vector &target_hsp, const std::set &taxon_ids) = 0; + virtual void add(const std::vector &target_hsp, const std::set &taxon_ids) = 0; virtual ~TargetCulling() = default; enum { FINISHED = 0, NEXT = 1, INCLUDE = 2}; - static TargetCulling* get(); + static TargetCulling* get(const int64_t max_target_seqs); }; struct GlobalCulling : public TargetCulling { - GlobalCulling() : + GlobalCulling(const int64_t max_target_seqs) : + max_target_seqs_(max_target_seqs), n_(0), top_score_(0) {} @@ -61,9 +62,9 @@ struct GlobalCulling : public TargetCulling if (config.toppercent < 100.0) return (1.0 - score_matrix.bitscore(t.filter_score) / top_score_) * 100.0 <= config.toppercent ? INCLUDE : FINISHED; else - return n_ < config.max_alignments ? INCLUDE : FINISHED; + return n_ < max_target_seqs_ ? INCLUDE : FINISHED; } - virtual int cull(const vector &target_hsp, const std::set &taxon_ids) const + virtual int cull(const std::vector &target_hsp, const std::set &taxon_ids) const { if (top_score_ == 0.0) return INCLUDE; @@ -82,7 +83,7 @@ struct GlobalCulling : public TargetCulling else if (config.toppercent < 100.0) return (1.0 - score_matrix.bitscore(target_hsp[0].score) / top_score_) * 100.0 <= config.toppercent ? INCLUDE : FINISHED; else - return n_ < config.max_alignments ? INCLUDE : FINISHED; + return n_ < max_target_seqs_ ? INCLUDE : FINISHED; } virtual void add(const Target &t) { @@ -93,7 +94,7 @@ struct GlobalCulling : public TargetCulling for (unsigned i : t.taxon_rank_ids) ++taxon_count_[i]; } - virtual void add(const vector &target_hsp, const std::set &taxon_ids) + virtual void add(const std::vector &target_hsp, const std::set &taxon_ids) { if (top_score_ == 0) top_score_ = score_matrix.bitscore(target_hsp[0].score); @@ -104,15 +105,16 @@ struct GlobalCulling : public TargetCulling } virtual ~GlobalCulling() = default; private: - size_t n_; + const int64_t max_target_seqs_; + int64_t n_; double top_score_; std::map taxon_count_; }; struct RangeCulling : public TargetCulling { - RangeCulling() : - p_((int)std::min(config.max_alignments, (size_t)INT_MAX)) + RangeCulling(const int64_t max_target_seqs) : + p_(max_target_seqs) {} virtual int cull(const Target &t) const { @@ -129,7 +131,7 @@ struct RangeCulling : public TargetCulling } return (double)c / l * 100.0 < config.query_range_cover ? INCLUDE : NEXT; } - virtual int cull(const std::vector &target_hsp, const std::set &taxon_ids) const + virtual int cull(const std::vector &target_hsp, const std::set &taxon_ids) const { int c = 0, l = 0; for (std::vector::const_iterator i = target_hsp.begin(); i != target_hsp.end(); ++i) { @@ -148,7 +150,7 @@ struct RangeCulling : public TargetCulling for (std::list::const_iterator i = t.hsps.begin(); i != t.hsps.end(); ++i) p_.insert(i->query_source_range, i->score); } - virtual void add(const vector &target_hsp, const std::set &taxon_ids) + virtual void add(const std::vector &target_hsp, const std::set &taxon_ids) { for (std::vector::const_iterator i = target_hsp.begin(); i != target_hsp.end(); ++i) p_.insert(i->absolute_query_range(), i->score); diff --git a/src/output/taxon_format.cpp b/src/output/taxon_format.cpp index b986d862e..0a7b1a44a 100644 --- a/src/output/taxon_format.cpp +++ b/src/output/taxon_format.cpp @@ -25,16 +25,17 @@ along with this program. If not, see . using std::set; using std::endl; +using std::vector; -void Taxon_format::print_match(const HspContext &r, const Search::Config& cfg, TextBuffer &out) +void Taxon_format::print_match(const HspContext &r, Output::Info &info) { - const vector taxons(cfg.db->taxids(r.subject_oid)); + const vector taxons(info.db->taxids(r.subject_oid)); if (taxons.empty()) return; evalue = std::min(evalue, r.evalue()); try { - for (vector::const_iterator i = taxons.begin(); i != taxons.end(); ++i) - taxid = cfg.taxon_nodes->get_lca(taxid, *i); + for (vector::const_iterator i = taxons.begin(); i != taxons.end(); ++i) + taxid = info.db->taxon_nodes().get_lca(taxid, *i); } catch (std::exception &) { std::cerr << "Query=" << r.query_title << endl << "Subject=" << r.target_title << endl; @@ -42,13 +43,13 @@ void Taxon_format::print_match(const HspContext &r, const Search::Config& cfg, T } } -void Taxon_format::print_query_epilog(TextBuffer &out, const char *query_title, bool unaligned, const Search::Config ¶ms) const +void Taxon_format::print_query_epilog(Output::Info &info) const { - out.write_until(query_title, Util::Seq::id_delimiters); - out << '\t' << taxid << '\t'; + info.out.write_until(info.query.title, Util::Seq::id_delimiters); + info.out << '\t' << taxid << '\t'; if (taxid != 0) - out.print_e(evalue); + info.out.print_e(evalue); else - out << '0'; - out << '\n'; + info.out << '0'; + info.out << '\n'; } \ No newline at end of file diff --git a/src/output/xml_format.cpp b/src/output/xml_format.cpp index 2d801477d..c92e60003 100644 --- a/src/output/xml_format.cpp +++ b/src/output/xml_format.cpp @@ -6,9 +6,11 @@ #include "../util/sequence/sequence.h" using std::endl; +using std::string; -void XML_format::print_match(const HspContext& r, const Search::Config& metadata, TextBuffer& out) +void XML_format::print_match(const HspContext& r, Output::Info& info) { + auto& out = info.out; if (r.hsp_num == 0) { if (r.hit_num > 0) out << " " << '\n' << "" << '\n'; @@ -21,7 +23,7 @@ void XML_format::print_match(const HspContext& r, const Search::Config& metadata if (config.xml_blord_format) { out << " gnl|BL_ORD_ID|" << r.subject_oid << "" << '\n' << " "; - Output_format::print_title(out, target_seqid.c_str(), true, true, " >", &EscapeSequences::XML); + OutputFormat::print_title(out, target_seqid.c_str(), true, true, " >", &EscapeSequences::XML); out << "" << '\n'; } else { @@ -29,7 +31,7 @@ void XML_format::print_match(const HspContext& r, const Search::Config& metadata print_escaped(out, id, &EscapeSequences::XML); out << "" << '\n' << " "; - Output_format::print_title(out, def.c_str(), true, true, " >", &EscapeSequences::XML); + OutputFormat::print_title(out, def.c_str(), true, true, " >", &EscapeSequences::XML); out << "" << '\n'; } out << " "; @@ -107,29 +109,29 @@ void XML_format::print_header(Consumer& f, int mode, const char* matrix, int gap f.consume(ss.str().c_str(), ss.str().length()); } -void XML_format::print_query_intro(size_t query_num, const char* query_name, unsigned query_len, TextBuffer& out, bool unaligned, const Search::Config& cfg) const +void XML_format::print_query_intro(Output::Info& info) const { - out << "" << '\n' - << " " << query_num + 1 << "" << '\n' - << " Query_" << query_num + 1 << "" << '\n' + info.out << "" << '\n' + << " " << info.query.oid + 1 << "" << '\n' + << " Query_" << info.query.oid + 1 << "" << '\n' << " "; - print_title(out, query_name, true, false, "", &EscapeSequences::XML); - out << "" << '\n' - << " " << query_len << "" << '\n' + print_title(info.out, info.query.title, true, false, "", &EscapeSequences::XML); + info.out << "" << '\n' + << " " << info.query.len << "" << '\n' << "" << '\n'; } -void XML_format::print_query_epilog(TextBuffer& out, const char* query_title, bool unaligned, const Search::Config& parameters) const +void XML_format::print_query_epilog(Output::Info& info) const { - if (!unaligned) { - out << " " << '\n' + if (!info.unaligned) { + info.out << " " << '\n' << "" << '\n'; } - ((out << "" << '\n' + ((info.out << "" << '\n' << " " << '\n' << " " << '\n' - << " " << parameters.db_seqs << "" << '\n' - << " " << parameters.db_letters << "" << '\n' + << " " << info.db->sequence_count() << "" << '\n' + << " " << info.db->letters() << "" << '\n' << " 0" << '\n' << " 0" << '\n' << " ").print_d(score_matrix.k()) << "" << '\n' diff --git a/src/run/config.cpp b/src/run/config.cpp index 61e966053..ea47ddfbe 100644 --- a/src/run/config.cpp +++ b/src/run/config.cpp @@ -1,6 +1,6 @@ #include "config.h" #include "../basic/config.h" -#include "../data/block.h" +#include "../data/block/block.h" #include "../data/taxonomy_nodes.h" #include "../data/sequence_file.h" #include "../search/hit.h" @@ -10,8 +10,13 @@ #include "../align/global_ranking/global_ranking.h" #include "../search/search.h" #include "../masking/masking.h" +#include "../align/def.h" +#include "../dna/dna_index.h" + using std::endl; +using std::runtime_error; +using std::string; namespace Search { @@ -23,11 +28,10 @@ Config::Config() : soft_masking(MaskingAlgo::NONE), lazy_masking(false), track_aligned_queries(false), + max_target_seqs(config.max_target_seqs_.get(25)), db(nullptr), query_file(nullptr), out(nullptr), - taxon_nodes(nullptr), - taxonomy_scientific_names(nullptr), iteration_query_aligned(0) { if (config.iterate.present()) { @@ -74,18 +78,25 @@ Config::Config() : if (config.target_indexed && config.algo != ::Config::Algo::AUTO && config.algo != ::Config::Algo::DOUBLE_INDEXED) throw std::runtime_error("--target-indexed requires --algo 0"); - const MaskingMode masking_mode = from_string(config.masking); - switch (masking_mode) { - case MaskingMode::BLAST_SEG: - query_masking = MaskingAlgo::NONE; - target_masking = MaskingAlgo::SEG; - break; - case MaskingMode::TANTAN: - query_masking = MaskingAlgo::TANTAN; - target_masking = MaskingAlgo::TANTAN; - default: - ; - } + if(config.command != ::Config::blastn) { + const MaskingMode masking_mode = from_string(config.masking_.get("tantan")); + switch (masking_mode) { + case MaskingMode::BLAST_SEG: + query_masking = MaskingAlgo::NONE; + target_masking = MaskingAlgo::SEG; + break; + case MaskingMode::TANTAN: + query_masking = MaskingAlgo::TANTAN; + target_masking = MaskingAlgo::TANTAN; + default:; + } + } + else { + if (config.gap_open == -1) + config.gap_open = 5; + if (config.gap_extend == -1) + config.gap_extend = 2; + } if (config.ext_.empty()) { if (config.global_ranking_targets || config.swipe_all) @@ -112,6 +123,12 @@ Config::Config() : throw std::runtime_error("Incompatible options: --freq-masking, --seed-cut."); if (config.freq_sd_ != 0.0 && !config.freq_masking) throw std::runtime_error("--freq-sd requires --freq-masking."); + + if (max_target_seqs == 0) + max_target_seqs = INT64_MAX; + + if (config.minimizer_window_ && config.algo == ::Config::Algo::CTG_SEED) + throw runtime_error("Minimizer setting is not compatible with contiguous seed mode."); } Config::~Config() { @@ -120,10 +137,6 @@ Config::~Config() { void Config::free() { - delete taxon_nodes; - delete taxonomy_scientific_names; - taxon_nodes = nullptr; - taxonomy_scientific_names = nullptr; } } \ No newline at end of file diff --git a/src/run/config.h b/src/run/config.h index ae1db5af7..e3c8ed4fc 100644 --- a/src/run/config.h +++ b/src/run/config.h @@ -22,14 +22,18 @@ along with this program. If not, see . #pragma once #include #include +#include #include "../util/data_structures/bit_vector.h" #include "../util/scores/cutoff_table.h" +#include "../stats/dna_scoring/build_score.h" struct SequenceFile; struct Consumer; struct TextInputFile; struct Block; struct TaxonomyNodes; +struct ThreadPool; +struct OutputFormat; enum class Sensitivity; enum class SeedEncoding; enum class MaskingAlgo; @@ -38,12 +42,14 @@ template struct AsyncBuffer; struct Async; template struct Deque; -namespace Extension { +namespace Extension { enum class Mode; namespace GlobalRanking { struct Hit; }} - +namespace Dna{ + class Index; +} namespace Search { struct Hit; @@ -68,31 +74,49 @@ struct Config { bool lazy_masking; bool track_aligned_queries; double freq_sd; + Loc minimizer_window; unsigned hamming_filter_id; double ungapped_evalue; double ungapped_evalue_short; double gapped_filter_evalue; unsigned index_chunks; unsigned query_bins; + int64_t max_target_seqs; + std::unique_ptr output_format; std::shared_ptr db; - std::shared_ptr> query_file; + std::shared_ptr query_file; std::shared_ptr out; std::shared_ptr db_filter; - TaxonomyNodes* taxon_nodes; - std::vector* taxonomy_scientific_names; - std::unique_ptr query, target; + std::shared_ptr query, target; std::unique_ptr> query_skip; std::unique_ptr> seed_hit_buf; std::unique_ptr global_ranking_buffer; std::unique_ptr ranking_table; - - uint64_t db_seqs, db_letters, ref_blocks; + std::unique_ptr score_builder; +#ifdef WITH_DNA + std::unique_ptr dna_ref_index; +#endif + + int current_query_block; + int current_ref_block; + bool blocked_processing; + std::vector aligned_targets; + std::mutex aligned_targets_mtx; + + uint64_t db_seqs, db_letters, ref_blocks; +#ifdef UNGAPPED_SPOUGE + const Util::Scores::CutoffTable2D cutoff_table; +#else + Util::Scores::CutoffTable cutoff_table, cutoff_table_short; +#endif Util::Scores::CutoffTable cutoff_gapped1, cutoff_gapped2; Util::Scores::CutoffTable2D cutoff_gapped1_new, cutoff_gapped2_new; - size_t iteration_query_aligned; + BlockId iteration_query_aligned; + + std::unique_ptr thread_pool; bool iterated() const { return sensitivity.size() > 1; diff --git a/src/run/double_indexed.cpp b/src/run/double_indexed.cpp index 2ad4752de..1362f125d 100644 --- a/src/run/double_indexed.cpp +++ b/src/run/double_indexed.cpp @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2022 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -33,13 +33,12 @@ along with this program. If not, see . #include "../basic/statistics.h" #include "../basic/shape_config.h" #include "../util/seq_file_format.h" -#include "../data/load_seqs.h" #include "../output/output_format.h" #include "../data/frequent_seeds.h" #include "../output/daa/daa_write.h" #include "../data/taxonomy.h" #include "../masking/masking.h" -#include "../data/block.h" +#include "../data/block/block.h" #include "../search/search.h" #include "workflow.h" #include "../util/io/consumer.h" @@ -55,6 +54,10 @@ along with this program. If not, see . #include "../util/async_buffer.h" #include "config.h" #include "../data/seed_array.h" +#ifdef WITH_DNA +#include "../dna/dna_index.h" +#endif + using std::unique_ptr; using std::endl; @@ -63,7 +66,7 @@ using std::shared_ptr; namespace Search { -static const size_t MAX_INDEX_QUERY_SIZE = 32 * MEGABYTES; +static const int64_t MAX_INDEX_QUERY_SIZE = 32 * MEGABYTES; static const size_t MAX_HASH_SET_SIZE = 8 * MEGABYTES; static const size_t MIN_QUERY_INDEXED_DB_SIZE = 256 * MEGABYTES; @@ -95,43 +98,47 @@ static string get_ref_block_tmpfile_name(size_t query, size_t block) { } static void run_ref_chunk(SequenceFile &db_file, - const unsigned query_chunk, const unsigned query_iteration, - char *query_buffer, Consumer &master_out, PtrVector &tmp_file, Config& cfg) { + task_timer timer; log_rss(); - auto& ref_seqs = cfg.target->seqs(); auto& query_seqs = cfg.query->seqs(); - if (config.comp_based_stats == Stats::CBS::COMP_BASED_STATS_AND_MATRIX_ADJUST || flag_any(output_format->flags, Output::Flags::TARGET_SEQS)) { - cfg.target->unmasked_seqs() = ref_seqs; +#ifndef KEEP_TARGET_ID + if (config.lin_stage1 && !config.kmer_ranking && cfg.target.unique()) { + timer.go("Length sorting reference"); + cfg.target.reset(cfg.target->length_sorted(config.threads_)); + } +#endif + + if (config.comp_based_stats == Stats::CBS::COMP_BASED_STATS_AND_MATRIX_ADJUST || flag_any(cfg.output_format->flags, Output::Flags::TARGET_SEQS)) { + cfg.target->unmasked_seqs() = cfg.target->seqs(); cfg.target->unmasked_seqs().convert_all_to_std_alph(config.threads_); } - task_timer timer; if (cfg.target_masking != MaskingAlgo::NONE && !cfg.lazy_masking) { timer.go("Masking reference"); - size_t n = mask_seqs(ref_seqs, Masking::get(), true, cfg.target_masking); + size_t n = mask_seqs(cfg.target->seqs(), Masking::get(), true, cfg.target_masking); timer.finish(); log_stream << "Masked letters: " << n << endl; } - if (flag_any(output_format->flags, Output::Flags::SELF_ALN_SCORES)) { + if (flag_any(cfg.output_format->flags, Output::Flags::SELF_ALN_SCORES)) { timer.go("Computing self alignment scores"); cfg.target->compute_self_aln(); } - const bool daa = *output_format == Output_format::daa; + const bool daa = *cfg.output_format == OutputFormat::daa; const bool persist_dict = daa || cfg.iterated(); - if(((blocked_processing || daa) && !config.global_ranking_targets) || cfg.iterated()) { + if(((cfg.blocked_processing || daa) && !config.global_ranking_targets) || cfg.iterated()) { timer.go("Initializing dictionary"); - if (config.multiprocessing || (current_ref_block == 0 && (!daa || query_chunk == 0) && query_iteration == 0)) - db_file.init_dict(query_chunk, current_ref_block); + if (config.multiprocessing || (cfg.current_ref_block == 0 && (!daa || cfg.current_query_block == 0) && query_iteration == 0)) + db_file.init_dict(cfg.current_query_block, cfg.current_ref_block); if(!config.global_ranking_targets) - db_file.init_dict_block(current_ref_block, ref_seqs.size(), persist_dict); + db_file.init_dict_block(cfg.current_ref_block, cfg.target->seqs().size(), persist_dict); } timer.go("Initializing temporary storage"); @@ -146,14 +153,15 @@ static void run_ref_chunk(SequenceFile &db_file, if (!config.swipe_all) { timer.go("Building reference histograms"); if(query_seeds_bitset.get()) - cfg.target->hst() = SeedHistogram(*cfg.target, true, query_seeds_bitset.get(), cfg.seed_encoding, nullptr, false, cfg.seed_complexity_cut, MaskingAlgo::NONE); + cfg.target->hst() = SeedHistogram(*cfg.target, true, query_seeds_bitset.get(), cfg.seed_encoding, nullptr, false, cfg.seed_complexity_cut, MaskingAlgo::NONE, cfg.minimizer_window); else if (query_seeds_hashed.get()) - cfg.target->hst() = SeedHistogram(*cfg.target, true, query_seeds_hashed.get(), cfg.seed_encoding, nullptr, false, cfg.seed_complexity_cut, MaskingAlgo::NONE); + cfg.target->hst() = SeedHistogram(*cfg.target, true, query_seeds_hashed.get(), cfg.seed_encoding, nullptr, false, cfg.seed_complexity_cut, MaskingAlgo::NONE, cfg.minimizer_window); else - cfg.target->hst() = SeedHistogram(*cfg.target, false, &no_filter, cfg.seed_encoding, nullptr, false, cfg.seed_complexity_cut, cfg.soft_masking); + cfg.target->hst() = SeedHistogram(*cfg.target, false, &no_filter, cfg.seed_encoding, nullptr, false, cfg.seed_complexity_cut, cfg.soft_masking, cfg.minimizer_window); timer.go("Allocating buffers"); - char *ref_buffer = SeedArray::alloc_buffer(cfg.target->hst(), cfg.index_chunks); + char* ref_buffer = SeedArray::alloc_buffer(cfg.target->hst(), cfg.index_chunks), + * query_buffer = SeedArray::alloc_buffer(cfg.query->hst(), cfg.index_chunks); timer.finish(); ::HashedSeedSet* target_seeds = nullptr; @@ -162,29 +170,36 @@ static void run_ref_chunk(SequenceFile &db_file, target_seeds = new ::HashedSeedSet(db_file.file_name() + ".seed_idx"); timer.finish(); } - - for (unsigned i = 0; i < shapes.count(); ++i) { - if(config.global_ranking_targets) - cfg.global_ranking_buffer.reset(new Config::RankingBuffer()); - search_shape(i, query_chunk, query_iteration, query_buffer, ref_buffer, cfg, target_seeds); - if (config.global_ranking_targets) - Extension::GlobalRanking::update_table(cfg); - } + if((config.command != ::Config::blastn)){ + for (unsigned i = 0; i < shapes.count(); ++i) { + if(config.global_ranking_targets) + cfg.global_ranking_buffer.reset(new Config::RankingBuffer()); + search_shape(i, cfg.current_query_block, query_iteration, query_buffer, ref_buffer, cfg, target_seeds); //index_targets(0,cfg,ref_buffer,target_seeds); + if (config.global_ranking_targets) + Extension::GlobalRanking::update_table(cfg); + } + } +#ifdef WITH_DNA + else + cfg.dna_ref_index = std::make_unique(cfg,ref_buffer); +#endif + timer.go("Deallocating buffers"); delete[] ref_buffer; + delete[] query_buffer; delete target_seeds; timer.go("Clearing query masking"); Frequent_seeds::clear_masking(query_seqs); } - Consumer* out; - const bool temp_output = (blocked_processing || cfg.iterated()) && !config.global_ranking_targets; + Consumer* out; + const bool temp_output = (cfg.blocked_processing || cfg.iterated()) && !config.global_ranking_targets; if (temp_output) { timer.go("Opening temporary output file"); if (config.multiprocessing) { - const string file_name = get_ref_block_tmpfile_name(query_chunk, current_ref_block); + const string file_name = get_ref_block_tmpfile_name(cfg.current_query_block, cfg.current_ref_block); tmp_file.push_back(new TempFile(file_name)); } else { tmp_file.push_back(new TempFile()); @@ -214,8 +229,7 @@ static void run_ref_chunk(SequenceFile &db_file, timer.finish(); } -static void run_query_iteration(const unsigned query_chunk, - const unsigned query_iteration, +static void run_query_iteration(const unsigned query_iteration, Consumer& master_out, OutputFile* unaligned_file, OutputFile* aligned_file, @@ -225,17 +239,17 @@ static void run_query_iteration(const unsigned query_chunk, task_timer timer; auto P = Parallelizer::get(); auto& query_seqs = options.query->seqs(); - auto& query_ids = options.query->ids(); auto& db_file = *options.db; if (query_iteration > 0) options.query_skip.reset(new vector (query_aligned)); if (config.algo == ::Config::Algo::AUTO && - (!sensitivity_traits.at(config.sensitivity).support_query_indexed + (!sensitivity_traits[(int)align_mode.sequence_type].at(config.sensitivity).support_query_indexed || query_seqs.letters() > MAX_INDEX_QUERY_SIZE || options.db_letters < MIN_QUERY_INDEXED_DB_SIZE || config.target_indexed - || config.swipe_all)) + || config.swipe_all + || options.minimizer_window)) config.algo = ::Config::Algo::DOUBLE_INDEXED; if (config.algo == ::Config::Algo::AUTO || config.algo == ::Config::Algo::QUERY_INDEXED) { timer.go("Building query seed set"); @@ -257,17 +271,19 @@ static void run_query_iteration(const unsigned query_chunk, timer.finish(); } - ::Config::set_option(options.index_chunks, config.lowmem_, 0u, config.algo == ::Config::Algo::DOUBLE_INDEXED ? sensitivity_traits.at(options.sensitivity[query_iteration]).index_chunks : 1u); + ::Config::set_option(options.index_chunks, config.lowmem_, 0u, config.algo == ::Config::Algo::DOUBLE_INDEXED ? sensitivity_traits[(int)align_mode.sequence_type].at(options.sensitivity[query_iteration]).index_chunks : 1u); options.lazy_masking = config.algo != ::Config::Algo::DOUBLE_INDEXED && options.target_masking != MaskingAlgo::NONE && config.frame_shift == 0; + if (config.command != ::Config::blastn){ + options.cutoff_gapped1 = { config.gapped_filter_evalue1 }; + options.cutoff_gapped2 = { options.gapped_filter_evalue }; + options.cutoff_gapped1_new = { config.gapped_filter_evalue1 }; + options.cutoff_gapped2_new = { options.gapped_filter_evalue }; + } - options.cutoff_gapped1 = { config.gapped_filter_evalue1 }; - options.cutoff_gapped2 = { options.gapped_filter_evalue }; - options.cutoff_gapped1_new = { config.gapped_filter_evalue1 }; - options.cutoff_gapped2_new = { options.gapped_filter_evalue }; - if (current_query_chunk == 0 && query_iteration == 0) { + if (options.current_query_block == 0 && query_iteration == 0) { message_stream << "Algorithm: " << to_string(config.algo) << endl; - if (config.freq_masking) + if (config.freq_masking && !config.lin_stage1) verbose_stream << "Seed frequency SD: " << options.freq_sd << endl; verbose_stream << "Shape configuration: " << ::shapes << endl; } @@ -277,30 +293,30 @@ static void run_query_iteration(const unsigned query_chunk, options.ranking_table.reset(new Search::Config::RankingTable(query_seqs.size() * config.global_ranking_targets / align_mode.query_contexts)); } - char* query_buffer = nullptr; if (!config.swipe_all && !config.target_indexed) { timer.go("Building query histograms"); - options.query->hst() = SeedHistogram(*options.query, false, &no_filter, options.seed_encoding, options.query_skip.get(), false, options.seed_complexity_cut, options.soft_masking); - - timer.go("Allocating buffers"); - query_buffer = SeedArray::alloc_buffer(options.query->hst(), options.index_chunks); + options.query->hst() = SeedHistogram(*options.query, false, &no_filter, options.seed_encoding, options.query_skip.get(), false, options.seed_complexity_cut, options.soft_masking, options.minimizer_window); timer.finish(); } log_rss(); - db_file.set_seqinfo_ptr(0); bool mp_last_chunk = false; //const bool lazy_masking = config.algo == ::Config::Algo::QUERY_INDEXED && (config.masking == 1 || config.target_seg == 1) && (config.global_ranking_targets == 0); - const bool load_titles = db_file.load_titles() == SequenceFile::LoadTitles::SINGLE_PASS || config.no_self_hits; + SequenceFile::LoadFlags load_flags = SequenceFile::LoadFlags::SEQS; + if (!flag_any(db_file.format_flags(), SequenceFile::FormatFlags::TITLES_LAZY) || config.no_self_hits) + load_flags |= SequenceFile::LoadFlags::TITLES; + if (options.lazy_masking) + load_flags |= SequenceFile::LoadFlags::LAZY_MASKING; if (config.multiprocessing) { - P->create_stack_from_file(stack_align_todo, get_ref_part_file_name(stack_align_todo, query_chunk)); + db_file.set_seqinfo_ptr(0); + P->create_stack_from_file(stack_align_todo, get_ref_part_file_name(stack_align_todo, options.current_query_block)); auto work = P->get_stack(stack_align_todo); - P->create_stack_from_file(stack_align_wip, get_ref_part_file_name(stack_align_wip, query_chunk)); + P->create_stack_from_file(stack_align_wip, get_ref_part_file_name(stack_align_wip, options.current_query_block)); auto wip = P->get_stack(stack_align_wip); - P->create_stack_from_file(stack_align_done, get_ref_part_file_name(stack_align_done, query_chunk)); + P->create_stack_from_file(stack_align_done, get_ref_part_file_name(stack_align_done, options.current_query_block)); auto done = P->get_stack(stack_align_done); - P->create_stack_from_file(stack_join_todo, get_ref_part_file_name(stack_join_todo, query_chunk)); + P->create_stack_from_file(stack_join_todo, get_ref_part_file_name(stack_join_todo, options.current_query_block)); auto join_work = P->get_stack(stack_join_todo); string buf; @@ -310,21 +326,31 @@ static void run_query_iteration(const unsigned query_chunk, Chunk chunk = to_chunk(buf); - P->log("SEARCH BEGIN " + std::to_string(query_chunk) + " " + std::to_string(chunk.i)); - - options.target.reset(db_file.load_seqs((size_t)(0), load_titles, options.db_filter.get(), true, options.lazy_masking, chunk)); - run_ref_chunk(db_file, query_chunk, query_iteration, query_buffer, master_out, tmp_file, options); + P->log("SEARCH BEGIN " + std::to_string(options.current_query_block) + " " + std::to_string(chunk.i)); + + options.target.reset(db_file.load_seqs((size_t)(0), options.db_filter.get(), load_flags, chunk)); + options.current_ref_block = chunk.i; + options.blocked_processing = true; + if (!config.mp_self || chunk.i >= options.current_query_block) + run_ref_chunk(db_file, query_iteration, master_out, tmp_file, options); + else { + const string file_name = get_ref_block_tmpfile_name(options.current_query_block, options.current_ref_block); + tmp_file.push_back(new TempFile(file_name)); + tmp_file.back().write(IntermediateRecord::FINISHED); + db_file.init_dict(options.current_query_block, options.current_ref_block); + db_file.close_dict_block(false); + } tmp_file.back().close(); size_t size_after_push = 0; done->push(buf, size_after_push); - if (size_after_push == db_file.get_n_partition_chunks()) { + if (size_after_push == (size_t)db_file.get_n_partition_chunks()) { join_work->push("TOKEN"); } wip->remove(buf); - P->log("SEARCH END " + std::to_string(query_chunk) + " " + std::to_string(chunk.i)); + P->log("SEARCH END " + std::to_string(options.current_query_block) + " " + std::to_string(chunk.i)); log_rss(); } @@ -334,20 +360,37 @@ static void run_query_iteration(const unsigned query_chunk, P->delete_stack(stack_align_done); } else { - for (current_ref_block = 0; ; ++current_ref_block) { - options.target.reset(db_file.load_seqs((size_t)(config.chunk_size * 1e9), load_titles, options.db_filter.get(), true, options.lazy_masking)); + /*if (config.self && !config.lin_stage1 && !db_file.eof()) + db_file.set_seqinfo_ptr(options.query->oid_end()); + else if (!config.self || options.current_query_block != 0 || !db_file.eof()) + db_file.set_seqinfo_ptr(0);*/ + db_file.set_seqinfo_ptr((config.self && !config.lin_stage1) ? options.query->oid_end() : 0); + for (options.current_ref_block = 0; ; ++options.current_ref_block) { + if (config.self && ((config.lin_stage1 && options.current_ref_block == options.current_query_block) || (!config.lin_stage1 && options.current_ref_block == 0))) { + options.target = options.query; + if (config.lin_stage1) + db_file.set_seqinfo_ptr(options.query->oid_end()); + } + else { + timer.go("Loading reference sequences"); + options.target.reset(db_file.load_seqs(config.block_size(), options.db_filter.get(), load_flags)); + } + if (options.current_ref_block == 0) { + db_file.reopen(); + const int64_t db_seq_count = options.db_filter ? options.db_filter->one_count() : options.db->sequence_count(); + options.blocked_processing = config.global_ranking_targets || options.target->seqs().size() < db_seq_count; + } if (options.target->empty()) break; - run_ref_chunk(db_file, query_chunk, query_iteration, query_buffer, master_out, tmp_file, options); + timer.finish(); + run_ref_chunk(db_file, query_iteration, master_out, tmp_file, options); } log_rss(); } timer.go("Deallocating buffers"); - delete[] query_buffer; query_seeds_hashed.reset(); query_seeds_bitset.reset(); options.query_skip.reset(); - delete Extension::memory; if (config.global_ranking_targets) { timer.go("Computing alignments"); @@ -364,8 +407,7 @@ static void run_query_iteration(const unsigned query_chunk, } } -static void run_query_chunk(const unsigned query_chunk, - Consumer &master_out, +static void run_query_chunk(Consumer &master_out, OutputFile *unaligned_file, OutputFile *aligned_file, Config &options) @@ -374,72 +416,69 @@ static void run_query_chunk(const unsigned query_chunk, task_timer timer; auto& db_file = *options.db; auto& query_seqs = options.query->seqs(); - auto& query_ids = options.query->ids(); PtrVector tmp_file; if (options.track_aligned_queries) { query_aligned.clear(); - query_aligned.insert(query_aligned.end(), query_ids.size(), false); + query_aligned.insert(query_aligned.end(), options.query->source_seq_count(), false); } - if(config.query_memory) - Extension::memory = new Extension::Memory(query_ids.size()); - if (flag_any(output_format->flags, Output::Flags::SELF_ALN_SCORES)) { + if (flag_any(options.output_format->flags, Output::Flags::SELF_ALN_SCORES)) { timer.go("Computing self alignment scores"); options.query->compute_self_aln(); } log_rss(); - size_t aligned = 0; - for (unsigned query_iteration = 0; query_iteration < options.sensitivity.size() && aligned < query_ids.size(); ++query_iteration) { + BlockId aligned = 0; + for (unsigned query_iteration = 0; query_iteration < options.sensitivity.size() && aligned < options.query->source_seq_count(); ++query_iteration) { setup_search(options.sensitivity[query_iteration], options); - run_query_iteration(query_chunk, query_iteration, master_out, unaligned_file, aligned_file, tmp_file, options); + run_query_iteration(query_iteration, master_out, unaligned_file, aligned_file, tmp_file, options); if (options.iterated()) { aligned += options.iteration_query_aligned; - message_stream << "Aligned " << options.iteration_query_aligned << '/' << query_ids.size() << " queries in this iteration, " - << aligned << '/' << query_ids.size() << " total." << endl; + message_stream << "Aligned " << options.iteration_query_aligned << '/' << options.query->source_seq_count() << " queries in this iteration, " + << aligned << '/' << options.query->source_seq_count() << " total." << endl; options.iteration_query_aligned = 0; } } log_rss(); - if (blocked_processing || config.multiprocessing || options.iterated()) { + if (options.blocked_processing || config.multiprocessing || options.iterated()) { if(!config.global_ranking_targets) timer.go("Joining output blocks"); if (config.multiprocessing) { - P->create_stack_from_file(stack_join_todo, get_ref_part_file_name(stack_join_todo, query_chunk)); + P->create_stack_from_file(stack_join_todo, get_ref_part_file_name(stack_join_todo, options.current_query_block)); auto work = P->get_stack(stack_join_todo); string buf; if ((! file_exists("stop")) && (work->pop(buf) > 0)) { - P->log("JOIN BEGIN "+std::to_string(query_chunk)); + P->log("JOIN BEGIN "+std::to_string(options.current_query_block)); - P->create_stack_from_file(stack_join_wip, get_ref_part_file_name(stack_join_wip, query_chunk)); + P->create_stack_from_file(stack_join_wip, get_ref_part_file_name(stack_join_wip, options.current_query_block)); auto wip = P->get_stack(stack_join_wip); wip->clear(); - P->create_stack_from_file(stack_join_done, get_ref_part_file_name(stack_join_done, query_chunk)); + P->create_stack_from_file(stack_join_done, get_ref_part_file_name(stack_join_done, options.current_query_block)); auto done = P->get_stack(stack_join_done); done->clear(); wip->push(buf); work->clear(); - current_ref_block = db_file.get_n_partition_chunks(); + options.current_ref_block = db_file.get_n_partition_chunks(); vector tmp_file_names; - for (int64_t i=0; iprint_header(*query_chunk_out, align_mode.mode, config.matrix.c_str(), score_matrix.gap_open(), score_matrix.gap_extend(), config.max_evalue, query_ids::get()[0], // unsigned(align_mode.query_translated ? query_source_seqs::get()[0].length() : query_seqs::get()[0].length())); - join_blocks(current_ref_block, *query_chunk_out, tmp_file, options, db_file, tmp_file_names); + join_blocks(options.current_ref_block, *query_chunk_out, tmp_file, options, db_file, tmp_file_names); // if (*output_format == Output_format::daa) // // finish_daa(*static_cast(query_chunk_out), *db_file); @@ -459,12 +498,12 @@ static void run_query_chunk(const unsigned query_chunk, P->delete_stack(stack_join_wip); P->delete_stack(stack_join_done); - P->log("JOIN END "+std::to_string(query_chunk)); + P->log("JOIN END "+std::to_string(options.current_query_block)); } P->delete_stack(stack_join_todo); } else { if (!tmp_file.empty()) - join_blocks(current_ref_block, master_out, tmp_file, options, db_file); + join_blocks(options.current_ref_block, master_out, tmp_file, options, db_file); } } @@ -483,6 +522,7 @@ static void run_query_chunk(const unsigned query_chunk, static void master_thread(task_timer &total_timer, Config &options) { + log_rss(); SequenceFile* db_file = options.db.get(); if (config.multiprocessing && config.mp_recover) { const size_t max_assumed_query_chunks = 65536; @@ -532,25 +572,20 @@ static void master_thread(task_timer &total_timer, Config &options) } task_timer timer("Opening the input file", true); - const Sequence_file_format *format_n = nullptr; - bool paired_mode = false; if (!options.self) { if (config.query_file.empty() && !options.query_file) std::cerr << "Query file parameter (--query/-q) is missing. Input will be read from stdin." << endl; - if (!options.query_file) { - options.query_file.reset(new list); - for(const string& f : config.query_file) - options.query_file->emplace_back(f); - if (options.query_file->empty()) - options.query_file->emplace_back(""); - paired_mode = options.query_file->size() == 2; - } - format_n = guess_format(options.query_file->front()); + if (!options.query_file) + options.query_file.reset(SequenceFile::auto_create(config.query_file, SequenceFile::Flags(), SequenceFile::Metadata(), input_value_traits)); } - current_query_chunk = 0; - size_t query_file_offset = 0; - + options.current_query_block = 0; + OId query_file_offset = 0; + SequenceFile::LoadFlags load_flags = SequenceFile::LoadFlags::ALL; + if (config.store_query_quality) + load_flags |= SequenceFile::LoadFlags::QUALITY; + if (config.command == ::Config::blastn) + load_flags |= SequenceFile::LoadFlags::DNA_PRESERVATION; if (config.multiprocessing && config.mp_init) { task_timer timer("Counting query blocks", true); @@ -558,18 +593,17 @@ static void master_thread(task_timer &total_timer, Config &options) do { if (options.self) { db_file->set_seqinfo_ptr(query_file_offset); - options.query.reset(db_file->load_seqs((size_t)(config.chunk_size * 1e9), true, options.db_filter.get())); + options.query.reset(db_file->load_seqs((size_t)(config.chunk_size * 1e9), options.db_filter.get(), SequenceFile::LoadFlags::ALL)); query_file_offset = db_file->tell_seq(); - } else { - options.query.reset(new Block(options.query_file->begin(), options.query_file->end(), *format_n, (size_t)(config.chunk_size * 1e9), input_value_traits, config.store_query_quality, false, paired_mode ? 2 : 1)); - } + } else + options.query.reset(options.query_file->load_seqs((int64_t)(config.chunk_size * 1e9), nullptr, load_flags)); ++block_count; } while (!options.query->empty()); if (options.self) { db_file->set_seqinfo_ptr(0); query_file_offset = 0; } else { - options.query_file->front().rewind(); + options.query_file->set_seqinfo_ptr(0); } for (size_t i = 0; i < block_count - 1; ++i) { @@ -580,12 +614,10 @@ static void master_thread(task_timer &total_timer, Config &options) return; } - current_query_chunk = 0; - timer.go("Opening the output file"); if (!options.out) options.out.reset(new OutputFile(config.output_file, config.compressor())); - if (*output_format == Output_format::daa) + if (*options.output_format == OutputFormat::daa) init_daa(*static_cast(options.out.get())); unique_ptr unaligned_file, aligned_file; if (!config.unaligned.empty()) @@ -594,35 +626,44 @@ static void master_thread(task_timer &total_timer, Config &options) aligned_file = unique_ptr(new OutputFile(config.aligned_file)); timer.finish(); - for (;; ++current_query_chunk) { + for (;query_file_offset < db_file->sequence_count(); ++options.current_query_block) { + log_rss(); task_timer timer("Loading query sequences", true); if (options.self) { db_file->set_seqinfo_ptr(query_file_offset); - options.query.reset(db_file->load_seqs((size_t)(config.chunk_size * 1e9), true, options.db_filter.get())); + options.query.reset(db_file->load_seqs(config.block_size(), options.db_filter.get(), SequenceFile::LoadFlags::ALL)); query_file_offset = db_file->tell_seq(); } else - options.query.reset(new Block(options.query_file->begin(), options.query_file->end(), *format_n, (size_t)(config.chunk_size * 1e9), input_value_traits, config.store_query_quality, false, paired_mode ? 2 : 1)); + options.query.reset(options.query_file->load_seqs(config.block_size(), nullptr, load_flags)); if (options.query->empty()) break; timer.finish(); options.query->seqs().print_stats(); - if ((config.mp_query_chunk >= 0) && (current_query_chunk != (unsigned)config.mp_query_chunk)) + if ((config.mp_query_chunk >= 0) && (options.current_query_block != config.mp_query_chunk)) continue; - if (current_query_chunk == 0 && *output_format != Output_format::daa) - output_format->print_header(*options.out, align_mode.mode, config.matrix.c_str(), score_matrix.gap_open(), score_matrix.gap_extend(), config.max_evalue, options.query->ids()[0], +#ifndef KEEP_TARGET_ID + if (config.lin_stage1 && !config.kmer_ranking) { + timer.go("Length sorting queries"); + options.query.reset(options.query->length_sorted(config.threads_)); + timer.finish(); + } +#endif + + if (options.current_query_block == 0 && *options.output_format != OutputFormat::daa && options.query->has_ids()) + options.output_format->print_header(*options.out, align_mode.mode, config.matrix.c_str(), score_matrix.gap_open(), score_matrix.gap_extend(), config.max_evalue, options.query->ids()[0], unsigned(align_mode.query_translated ? options.query->source_seqs()[0].length() : options.query->seqs()[0].length())); - if (options.query_masking != MaskingAlgo::NONE && !options.self) { + if (options.query_masking != MaskingAlgo::NONE) { timer.go("Masking queries"); mask_seqs(options.query->seqs(), Masking::get(), true, options.query_masking); timer.finish(); } - run_query_chunk(current_query_chunk, *options.out, unaligned_file.get(), aligned_file.get(), options); + run_query_chunk(*options.out, unaligned_file.get(), aligned_file.get(), options); if (file_exists("stop")) { message_stream << "Encountered \'stop\' file, shutting down run" << endl; @@ -632,24 +673,31 @@ static void master_thread(task_timer &total_timer, Config &options) if (options.query_file.unique()) { timer.go("Closing the input file"); - for(TextInputFile& f : *options.query_file) - f.close(); + options.query_file->close(); } timer.go("Closing the output file"); - if (*output_format == Output_format::daa) { - db_file->init_random_access(current_query_chunk, 0); + if (*options.output_format == OutputFormat::daa) { + db_file->init_random_access(options.current_query_block, 0); finish_daa(*static_cast(options.out.get()), *db_file); db_file->end_random_access(); } else - output_format->print_footer(*options.out); + options.output_format->print_footer(*options.out); options.out->finalize(); if (unaligned_file.get()) unaligned_file->close(); if (aligned_file.get()) aligned_file->close(); + if (!config.unaligned_targets.empty()) { + timer.go("Writing unaligned targets"); + options.db->write_accession_list(options.aligned_targets, config.unaligned_targets); + } + + timer.go("Closing the database"); + options.db.reset(); + timer.go("Cleaning up"); options.free(); @@ -660,11 +708,12 @@ static void master_thread(task_timer &total_timer, Config &options) //print_warnings(); } -void run(const shared_ptr& db, const shared_ptr>& query, const shared_ptr& out, const shared_ptr& db_filter) +void run(const shared_ptr& db, const shared_ptr& query, const shared_ptr& out, const shared_ptr& db_filter) { task_timer total; - align_mode = Align_mode(Align_mode::from_command(config.command)); + align_mode = AlignMode(AlignMode::from_command(config.command)); + (align_mode.sequence_type == SequenceType::amino_acid) ? value_traits = amino_acid_traits : value_traits = nucleotide_traits; message_stream << "Temporary directory: " << TempFile::get_temp_dir() << endl; @@ -673,44 +722,53 @@ void run(const shared_ptr& db, const shared_ptrneeds_taxon_id_lists || taxon_filter || taxon_culling) + if (cfg.output_format->needs_taxon_id_lists || taxon_filter || taxon_culling) metadata_flags |= SequenceFile::Metadata::TAXON_MAPPING; - if (output_format->needs_taxon_nodes || taxon_filter || taxon_culling) + if (cfg.output_format->needs_taxon_nodes || taxon_filter || taxon_culling) metadata_flags |= SequenceFile::Metadata::TAXON_NODES; - if (output_format->needs_taxon_scientific_names) + if (cfg.output_format->needs_taxon_scientific_names) metadata_flags |= SequenceFile::Metadata::TAXON_SCIENTIFIC_NAMES; - if (output_format->needs_taxon_ranks || taxon_culling) + if (cfg.output_format->needs_taxon_ranks || taxon_culling) metadata_flags |= SequenceFile::Metadata::TAXON_RANKS; - Config cfg; - task_timer timer("Opening the database", 1); - SequenceFile::Flags flags(SequenceFile::Flags::NONE); - if (flag_any(output_format->flags, Output::Flags::ALL_SEQIDS)) + task_timer timer; + SequenceFile::Flags flags(SequenceFile::Flags::NEED_LETTER_COUNT); + if (flag_any(cfg.output_format->flags, Output::Flags::ALL_SEQIDS)) flags |= SequenceFile::Flags::ALL_SEQIDS; - if (flag_any(output_format->flags, Output::Flags::FULL_TITLES) || config.no_self_hits) + if (flag_any(cfg.output_format->flags, Output::Flags::FULL_TITLES) || config.no_self_hits) flags |= SequenceFile::Flags::FULL_TITLES; - if (flag_any(output_format->flags, Output::Flags::TARGET_SEQS)) + if (flag_any(cfg.output_format->flags, Output::Flags::TARGET_SEQS)) flags |= SequenceFile::Flags::TARGET_SEQS; - if (flag_any(output_format->flags, Output::Flags::SELF_ALN_SCORES)) + if (flag_any(cfg.output_format->flags, Output::Flags::SELF_ALN_SCORES)) flags |= SequenceFile::Flags::SELF_ALN_SCORES; + if (!config.unaligned_targets.empty()) + flags |= SequenceFile::Flags::OID_TO_ACC_MAPPING; if (db) { cfg.db = db; if (!query) cfg.self = true; } - else - cfg.db.reset(SequenceFile::auto_create(config.database, flags, metadata_flags)); + else { + timer.go("Opening the database"); + cfg.db.reset(SequenceFile::auto_create({ config.database }, flags, metadata_flags, value_traits)); + } + if (config.multiprocessing && cfg.db->type() == SequenceFile::Type::FASTA) + throw std::runtime_error("Multiprocessing mode is not compatible with FASTA databases."); cfg.db_seqs = cfg.db->sequence_count(); cfg.db_letters = cfg.db->letters(); cfg.ref_blocks = cfg.db->total_blocks(); cfg.query_file = query; cfg.db_filter = db_filter; cfg.out = out; + if (!config.unaligned_targets.empty()) + cfg.aligned_targets.insert(cfg.aligned_targets.begin(), cfg.db->sequence_count(), false); timer.finish(); message_stream << "Database: " << config.database << ' '; @@ -720,29 +778,29 @@ void run(const shared_ptr& db, const shared_ptrtaxon_nodes(); + if (cfg.output_format->needs_taxon_nodes || taxon_filter || taxon_culling) { if (taxon_filter) { timer.go("Building taxonomy filter"); - cfg.db_filter.reset(cfg.db->filter_by_taxonomy(config.taxonlist, config.taxon_exclude, *cfg.taxon_nodes)); + cfg.db_filter.reset(cfg.db->filter_by_taxonomy(config.taxonlist, config.taxon_exclude)); } timer.finish(); } - if (output_format->needs_taxon_scientific_names) { - timer.go("Loading taxonomy names"); - cfg.taxonomy_scientific_names = cfg.db->taxon_scientific_names(); - timer.finish(); - } if (!config.seqidlist.empty()) { + if (taxon_filter) + throw std::runtime_error("--seqidlist is not compatible with taxonomy filtering."); message_stream << "Filtering database by accession list: " << config.seqidlist << endl; timer.go("Building database filter"); cfg.db_filter.reset(cfg.db->filter_by_accession(config.seqidlist)); timer.finish(); } - master_thread(total, cfg); + if(align_mode.sequence_type == SequenceType::nucleotide) + cfg.score_builder = std::make_unique(config.match_reward, config.mismatch_penalty, config.gap_open, config.gap_extend,cfg.db_letters,cfg.db->sequence_count()); + + + master_thread(total, cfg); + log_rss(); } } diff --git a/src/run/main.cpp b/src/run/main.cpp index 56f92d399..130712238 100644 --- a/src/run/main.cpp +++ b/src/run/main.cpp @@ -1,6 +1,10 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2017 Benjamin Buchfink +Copyright (C) 2013-2022 Max Planck Society for the Advancement of Science e.V. + Benjamin Buchfink + Eberhard Karls Universitaet Tuebingen + +Code developed by Benjamin Buchfink This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,6 +21,9 @@ along with this program. If not, see . ****/ #include +#ifdef WITH_MIMALLOC +#include +#endif #include "../basic/config.h" #include "tools.h" #include "../data/reference.h" @@ -25,10 +32,13 @@ along with this program. If not, see . #include "../output/recursive_parser.h" #include "../util/simd.h" #include "../data/dmnd/dmnd.h" +#include "../util/command_line_parser.h" using std::cout; using std::cerr; using std::endl; +using std::runtime_error; +using std::string; void model_seqs(); void opt(); @@ -48,9 +58,7 @@ void translate(); void filter_blasttab(); void show_cbs(); void reverse(); -void get_medoids_from_tree(); void roc(); -void merge_tsv(); void roc_id(); void makeindex(); void find_shapes(); @@ -58,24 +66,32 @@ void composition(); void join(); void hash_seqs(); void list_seeds(); -void merge_daa(); -#ifdef WITH_BLASTDB -void prep_blast_db(); +void prep_db(); +void greedy_vertex_cover(); +#ifdef EXTRA +void index_fasta(); +void fetch_seq(); +void length_sort(); +void sort(); #endif void split(); namespace Benchmark { DECL_DISPATCH(void, benchmark, ()) } -namespace Util { namespace Algo { namespace UPGMA { void upgma(); } } } -namespace Util { namespace Algo { namespace UPGMA_MC { void upgma(); } } } namespace Test { int run(); -void simulate_seqs(); -void mutate(); } +namespace Cluster { +void reassign(); +void realign(); +void recluster(); +namespace Incremental { +}} int main(int ac, const char* av[]) { try { - config = Config(ac, av); + CommandLineParser parser; + config = Config(ac, av, true, parser); + switch (config.command) { case Config::help: @@ -93,10 +109,12 @@ int main(int ac, const char* av[]) case Config::view: if (!config.daa_file.empty()) view_daa(); +#ifdef EXTRA else if (!config.input_ref_file.empty()) view_tsv(); +#endif else - throw std::runtime_error("The view command requires either a text (option --in) or DAA (option -a) input file."); + throw std::runtime_error("The view command requires a DAA (option -a) input file."); break; case Config::getseq: get_seq(); @@ -104,12 +122,6 @@ int main(int ac, const char* av[]) case Config::random_seqs: random_seqs(); break; - case Config::sort: - sort_file(); - break; - case Config::db_stat: - db_stat(); - break; case Config::mask: run_masker(); break; @@ -129,10 +141,11 @@ int main(int ac, const char* av[]) pairwise(); break; case Config::cluster: + case Config::DEEPCLUST: // Why is cluster_similarity not set at the end of the Config constructor? if(!config.cluster_similarity.empty()){ string expression = RecursiveParser::clean_expression(&config.cluster_similarity); - RecursiveParser rp(nullptr, expression.c_str(), true); + RecursiveParser rp(nullptr, expression.c_str()); try{ rp.evaluate(); } @@ -141,7 +154,7 @@ int main(int ac, const char* av[]) throw e; } } - Workflow::Cluster::ClusterRegistry::get(config.cluster_algo)->run(); + Workflow::Cluster::ClusterRegistry::get(config.cluster_algo.get("cascaded"))->run(); break; case Config::translate: translate(); @@ -152,39 +165,21 @@ int main(int ac, const char* av[]) case Config::show_cbs: show_cbs(); break; - case Config::simulate_seqs: - Test::simulate_seqs(); - break; case Config::benchmark: Benchmark::benchmark(); break; case Config::split: split(); break; - case Config::upgma: - Util::Algo::UPGMA::upgma(); - break; - case Config::upgma_mc: - Util::Algo::UPGMA_MC::upgma(); - break; case Config::regression_test: return Test::run(); break; case Config::reverse_seqs: reverse(); break; - case Config::compute_medoids: - get_medoids_from_tree(); - break; - case Config::mutate: - Test::mutate(); - break; case Config::roc: roc(); break; - case Config::merge_tsv: - merge_tsv(); - break; case Config::rocid: roc_id(); break; @@ -197,11 +192,9 @@ int main(int ac, const char* av[]) case Config::HASH_SEQS: hash_seqs(); break; -#ifdef WITH_BLASTDB - case Config::prep_blast_db: - prep_blast_db(); + case Config::prep_db: + prep_db(); break; -#endif case Config::composition: composition(); break; @@ -211,17 +204,46 @@ int main(int ac, const char* av[]) case Config::LIST_SEEDS: list_seeds(); break; - case Config::MERGE_DAA: - merge_daa(); + case Config::CLUSTER_REALIGN: + Cluster::realign(); + break; + case Config::GREEDY_VERTEX_COVER: + greedy_vertex_cover(); + break; + case Config::CLUSTER_REASSIGN: + Cluster::reassign(); + break; + case Config::RECLUSTER: + Cluster::recluster(); + break; +#ifdef EXTRA + case Config::INDEX_FASTA: + index_fasta(); + break; + case Config::FETCH_SEQ: + fetch_seq(); break; + case Config::blastn: + Search::run(); + break; + case Config::LENGTH_SORT: + length_sort(); + break; + case Config::sort: + sort(); + break; +#endif default: return 1; } } - catch(const std::bad_alloc &e) { + catch (const std::bad_alloc &e) { cerr << "Failed to allocate sufficient memory. Please refer to the manual for instructions on memory usage." << endl; log_stream << "Error: " << e.what() << endl; return 1; + } + catch (const FileOpenException&) { + return 1; } catch(const std::exception& e) { cerr << "Error: " << e.what() << endl; log_stream << "Error: " << e.what() << endl; diff --git a/src/run/tools.cpp b/src/run/tools.cpp index 98218cf81..4c734a889 100644 --- a/src/run/tools.cpp +++ b/src/run/tools.cpp @@ -30,7 +30,6 @@ along with this program. If not, see . #include "../data/sequence_set.h" #include "../util/seq_file_format.h" #include "../data/queries.h" -#include "../data/load_seqs.h" #include "../data/sequence_file.h" #include "../masking/masking.h" #include "../dp/dp.h" @@ -39,7 +38,7 @@ along with this program. If not, see . #include "../data/dmnd/dmnd.h" #include "../util/util.h" #include "../util/sequence/sequence.h" -#include "../basic/translate.h" +#include "../util/sequence/translate.h" using std::thread; using std::chrono::high_resolution_clock; @@ -48,10 +47,13 @@ using std::cout; using std::endl; using std::cerr; using std::unique_ptr; +using std::runtime_error; +using std::vector; +using std::string; void get_seq() { - SequenceFile* db_file = SequenceFile::auto_create(config.database); + SequenceFile* db_file = SequenceFile::auto_create({ config.database }); db_file->get_seq(); delete db_file; } @@ -85,46 +87,6 @@ void random_seqs() delete ref_seqs; } -void sort_file() -{ - TextInputFile f(config.single_query_file()); - vector > data; - while (f.getline(), !f.eof()) { - unsigned query; - sscanf(f.line.c_str(), "%u", &query); - data.push_back(Pair(query, f.line)); - } - std::stable_sort(data.begin(), data.end()); - for (vector >::const_iterator i = data.begin(); i != data.end(); ++i) - cout << i->second << endl; - f.close(); -} - -void db_stat() -{ - DatabaseFile db_file(config.database); - Block* ref_seqs = db_file.load_seqs(std::numeric_limits::max()); - const auto& r = ref_seqs->seqs(); - cout << "Sequences = " << r.size() << endl; - - size_t letters = 0; - vector letter_freq(20); - for (size_t i = 0; i < r.size(); ++i) { - const Sequence seq = r[i]; - for (Loc j = 0; j < seq.length(); ++j) { - if (seq[j] < 20) { - ++letters; - ++letter_freq[(int)seq[j]]; - } - } - } - cout << "Frequencies = "; - for (vector::const_iterator i = letter_freq.begin(); i != letter_freq.end(); ++i) - cout << (double)*i / letters << ','; - cout << endl; - -} - void run_masker() { TextInputFile f(config.single_query_file()); @@ -203,7 +165,7 @@ void read_sim() void info() { - vector arch_flags; + vector arch_flags; #ifdef __SSE2__ arch_flags.push_back("sse2"); #endif @@ -218,14 +180,14 @@ void info() #endif cout << "Architecture flags: "; - for (vector::const_iterator i = arch_flags.begin(); i != arch_flags.end(); ++i) + for (vector::const_iterator i = arch_flags.begin(); i != arch_flags.end(); ++i) cout << *i << ' '; cout << endl; } void pairwise_worker(TextInputFile *in, std::mutex *input_lock, std::mutex *output_lock) { FASTA_format format; - string id_r, id_q; + std::string id_r, id_q; vector ref, query; while(true) { @@ -239,10 +201,10 @@ void pairwise_worker(TextInputFile *in, std::mutex *input_lock, std::mutex *outp return; } input_lock->unlock(); - const string ir = Util::Seq::seqid(id_r.c_str(), false), iq = Util::Seq::seqid(id_q.c_str(), false); + const std::string ir = Util::Seq::seqid(id_r.c_str(), false), iq = Util::Seq::seqid(id_q.c_str(), false); Hsp hsp(true); //smith_waterman(Sequence(query), Sequence(ref), hsp); - HspContext context(hsp, 0, TranslatedSequence(query), "", 0, 0, nullptr, 0, 0, Sequence()); + HspContext context(hsp, 0, 0, TranslatedSequence(query), "", 0, 0, nullptr, 0, 0, Sequence()); HspContext::Iterator it = context.begin(); std::stringstream ss; while (it.good()) { @@ -267,7 +229,7 @@ void pairwise() TextInputFile in(config.single_query_file()); std::mutex input_lock, output_lock; vector threads; - for (unsigned i = 0; i < config.threads_; ++i) + for (int i = 0; i < config.threads_; ++i) threads.emplace_back(pairwise_worker, &in, &input_lock, &output_lock); for (auto &t : threads) t.join(); diff --git a/src/run/workflow.h b/src/run/workflow.h index d99167270..58ded77af 100644 --- a/src/run/workflow.h +++ b/src/run/workflow.h @@ -27,6 +27,6 @@ along with this program. If not, see . namespace Search { -void run(const std::shared_ptr& db = nullptr, const std::shared_ptr>& query = nullptr, const std::shared_ptr& out = nullptr, const std::shared_ptr& db_filter = nullptr); +void run(const std::shared_ptr& db = nullptr, const std::shared_ptr& query = nullptr, const std::shared_ptr& out = nullptr, const std::shared_ptr& db_filter = nullptr); } diff --git a/src/search/hit.h b/src/search/hit.h index fca11295b..987bc9a16 100644 --- a/src/search/hit.h +++ b/src/search/hit.h @@ -27,8 +27,6 @@ along with this program. If not, see . #include "../util/io/input_file.h" #include "../util/io/serialize.h" -// #define HIT_KEEP_TARGET_ID - namespace Search { #pragma pack(1) @@ -36,7 +34,7 @@ namespace Search { struct Hit { using Key = uint32_t; - using SeedOffset = uint32_t; + using SeedOffset = Loc; uint32_t query_; PackedLoc subject_; @@ -152,18 +150,19 @@ struct Hit } template<> struct SerializerTraits { + using Key = BlockId; SerializerTraits(bool long_subject_offsets, int32_t query_contexts): long_subject_offsets(long_subject_offsets), key{ query_contexts } {} const bool long_subject_offsets; - const struct Key { - int32_t operator()(const Search::Hit& hit) const { + const struct { + Key operator()(const Search::Hit& hit) const { return hit.query_ / query_contexts; } const int32_t query_contexts; } key; - static Search::Hit make_sentry(uint32_t query, uint32_t seed_offset) { + static Search::Hit make_sentry(uint32_t query, Loc seed_offset) { return { query, 0, seed_offset,0 }; } static bool is_sentry(const Search::Hit& hit) { @@ -243,9 +242,9 @@ template<> struct TypeDeserializer { #ifdef HIT_KEEP_TARGET_ID uint32_t target_block_id; f_->read(target_block_id); - *it = { query_id, subject_loc, seed_offset, score, target_block_id }; + *it = { query_id, subject_loc, (Loc)seed_offset, score, target_block_id }; #else - *it = { query_id, subject_loc, seed_offset, score }; + *it = { query_id, subject_loc, (Loc)seed_offset, score }; #endif } } diff --git a/src/search/kmer_ranking.cpp b/src/search/kmer_ranking.cpp new file mode 100644 index 000000000..0bbc44ab1 --- /dev/null +++ b/src/search/kmer_ranking.cpp @@ -0,0 +1,60 @@ +#include +#include +#include "kmer_ranking.h" +#include "../util/algo/join_result.h" +#include "../basic/config.h" + +using std::pair; +using std::vector; +using std::atomic_int; +using std::thread; +using std::greater; +using std::atomic; +using std::runtime_error; + +namespace Search { + +KmerRanking::KmerRanking(const SequenceSet& queries, DoubleArray *query_seed_hits, DoubleArray *ref_seed_hits) { +#ifdef KEEP_TARGET_ID + atomic_int seedp(0); + const auto query_count = queries.size(); + atomic* counts = new atomic[query_count]; + + auto worker = [&] { + int p; + while ((p = seedp++) < Const::seedp) { + for (auto it = JoinIterator(query_seed_hits[p].begin(), ref_seed_hits[p].begin()); it;) { + const Range query_hits = *it.r; + for (SeedLoc s : query_hits) { + float value = counts[s.block_id].load(); + while (!counts[s.block_id].compare_exchange_weak(value, value + (float)sqrt(it.s->size()))); + //counts[s.block_id] += it.s->size(); + } + ++it; + } + } + + }; + + vector threads; + for (size_t i = 0; i < config.threads_; ++i) + threads.emplace_back(worker); + for (auto& i : threads) + i.join(); + + rank_.reserve(query_count); + for (BlockId i = 0; i < query_count; ++i) + rank_.push_back(counts[i]); + delete[] counts; +#else + throw runtime_error("Option only available when compiled with KEEP_TARGET_ID=ON"); +#endif +} + +KmerRanking::KmerRanking(const SequenceSet& queries) { + rank_.reserve(queries.size()); + for (BlockId i = 0; i < queries.size(); ++i) + rank_.push_back((float)queries[i].length()); +} + +} \ No newline at end of file diff --git a/src/search/kmer_ranking.h b/src/search/kmer_ranking.h new file mode 100644 index 000000000..d6aee3bca --- /dev/null +++ b/src/search/kmer_ranking.h @@ -0,0 +1,32 @@ +#pragma once +#include "../util/data_structures/double_array.h" +#include "../data/flags.h" +#include "../data/sequence_set.h" + +namespace Search { + +struct KmerRanking { + + KmerRanking(const SequenceSet& queries, DoubleArray *query_seed_hits, DoubleArray *ref_seed_hits); + KmerRanking(const SequenceSet& queries); + + int highest_ranking(const SeedLoc* begin, const SeedLoc* end) { + ptrdiff_t r = 0; +#ifdef KEEP_TARGET_ID + float rank = rank_[begin->block_id]; + for (const SeedLoc* i = begin + 1; i < end; ++i) + if (rank_[i->block_id] > rank) { + rank = rank_[i->block_id]; + r = i - begin; + } +#endif + return (int)r; + } + +private: + + std::vector rank_; + +}; + +} \ No newline at end of file diff --git a/src/search/left_most.h b/src/search/left_most.h index f0fc743fc..661ae8df0 100644 --- a/src/search/left_most.h +++ b/src/search/left_most.h @@ -29,10 +29,10 @@ along with this program. If not, see . namespace Search { -static inline bool verify_hit(const Letter* q, const Letter* s, int score_cutoff, bool left, uint32_t match_mask, unsigned sid, bool chunked, unsigned hamming_filter_id) { +static inline bool verify_hit(const Letter* q, const Letter* s, int score_cutoff, bool left, uint32_t match_mask, int sid, bool chunked, unsigned hamming_filter_id) { if (chunked) { if ((shapes[sid].mask_ & match_mask) == shapes[sid].mask_) { - Packed_seed seed; + PackedSeed seed; if (!shapes[sid].set_seed(seed, s)) return false; if (left && !current_range.lower_or_equal(seed_partition(seed))) @@ -46,7 +46,7 @@ static inline bool verify_hit(const Letter* q, const Letter* s, int score_cutoff return id >= hamming_filter_id; } -static inline bool verify_hits(uint32_t mask, const Letter* q, const Letter* s, int score_cutoff, bool left, uint32_t match_mask, unsigned sid, bool chunked, unsigned hamming_filter_id) { +static inline bool verify_hits(uint32_t mask, const Letter* q, const Letter* s, int score_cutoff, bool left, uint32_t match_mask, int sid, bool chunked, unsigned hamming_filter_id) { int shift = 0; while (mask != 0) { int i = ctz(mask); @@ -64,22 +64,22 @@ static inline bool left_most_filter(const Sequence &query, const int seed_len, const Context &context, bool first_shape, - size_t shape_id, + const int shape_id, int score_cutoff, bool chunked, unsigned hamming_filter_id) { constexpr int WINDOW_LEFT = 16, WINDOW_RIGHT = 32; - int d = std::max(seed_offset - WINDOW_LEFT, 0), window_left = std::min(WINDOW_LEFT, seed_offset); + Loc d = std::max(seed_offset - WINDOW_LEFT, 0), window_left = std::min(WINDOW_LEFT, seed_offset); const Letter *q = query.data() + d, *s = subject + d; - int window = (int)query.length() - d; + Loc window = query.length() - d; window = std::min(window, window_left + 1 + WINDOW_RIGHT); const Sequence subject_clipped = Util::Seq::clip(s, window, window_left); - window -= s + window - subject_clipped.end(); + window -= Loc(s + window - subject_clipped.end()); - d = subject_clipped.data() - s; + d = Loc(subject_clipped.data() - s); q += d; s += d; window_left -= d; diff --git a/src/search/search.h b/src/search/search.h index 2b7d56af4..735fed0c7 100644 --- a/src/search/search.h +++ b/src/search/search.h @@ -35,6 +35,7 @@ along with this program. If not, see . #include "hit.h" #include "../util/data_structures/writer.h" #include "../data/flags.h" +#include "kmer_ranking.h" // #define UNGAPPED_SPOUGE @@ -50,6 +51,9 @@ struct SensitivityTraits { const unsigned query_bins; const char* contiguous_seed; const double seed_cut; + const double default_block_size; + const Reduction& reduction; + const int minimizer_window; }; struct HashedSeedSet; @@ -58,19 +62,16 @@ namespace Search { struct Context { const PatternMatcher previous_matcher, current_matcher; -#ifdef UNGAPPED_SPOUGE - const Util::Scores::CutoffTable2D cutoff_table; -#else - const Util::Scores::CutoffTable cutoff_table, cutoff_table_short; -#endif const int short_query_ungapped_cutoff; + KmerRanking* kmer_ranking; }; -extern const std::map sensitivity_traits; -extern const std::map> shape_codes; +extern const std::map sensitivity_traits[2]; +extern const std::map> shape_codes[2]; extern const std::map> iterated_sens; +extern const std::map> cluster_sens; -void search_shape(unsigned sid, unsigned query_block, unsigned query_iteration, char* query_buffer, char* ref_buffer, Config& cfg, const HashedSeedSet* target_seeds); +void search_shape(unsigned sid, int query_block, unsigned query_iteration, char* query_buffer, char* ref_buffer, Config& cfg, const HashedSeedSet* target_seeds); bool use_single_indexed(double coverage, size_t query_letters, size_t ref_letters); void setup_search(Sensitivity sens, Search::Config& cfg); MaskingAlgo soft_masking_algo(const SensitivityTraits& traits); @@ -109,9 +110,13 @@ struct WorkSet { std::vector> vq, vs; #endif FlatArray hits; + KmerRanking* kmer_ranking; }; -DECL_DISPATCH(void, stage1, (const SeedLoc* q, size_t nq, const SeedLoc* s, size_t ns, WorkSet& work_set)) +DECL_DISPATCH(void, stage1, (const SeedLoc* q, int32_t nq, const SeedLoc* s, int32_t ns, WorkSet& work_set)) +DECL_DISPATCH(void, stage1_lin, (const SeedLoc* q, int32_t nq, const SeedLoc* s, int32_t ns, WorkSet& work_set)) +DECL_DISPATCH(void, stage1_lin_ranked, (const SeedLoc* q, int32_t nq, const SeedLoc* s, int32_t ns, WorkSet& work_set)) +DECL_DISPATCH(void, stage1_self, (const SeedLoc* q, int32_t nq, const SeedLoc* s, int32_t ns, WorkSet& work_set)) } diff --git a/src/search/seed_complexity.cpp b/src/search/seed_complexity.cpp index 203ce14df..1517e8ca0 100644 --- a/src/search/seed_complexity.cpp +++ b/src/search/seed_complexity.cpp @@ -22,11 +22,12 @@ along with this program. If not, see . #include #include #include "seed_complexity.h" -#include "../data/block.h" +#include "../data/block/block.h" #include "../util/algo/join_result.h" #include "../util/string/string.h" using std::array; +using std::vector; using std::endl; extern double lnfact[]; @@ -67,7 +68,7 @@ bool Search::seed_is_complex_unreduced(Letter* seq, const Shape& shape, const do ++stats.low_complexity_seeds; return false; } - return true; + return true; } void Search::mask_seeds(const Shape& shape, const SeedPartitionRange& range, DoubleArray* query_seed_hits, DoubleArray* ref_seed_hits, Search::Config& cfg) @@ -110,7 +111,7 @@ void Search::mask_seeds(const Shape& shape, const SeedPartitionRange& range, Dou }; vector threads; - for (size_t i = 0; i < config.threads_; ++i) + for (int i = 0; i < config.threads_; ++i) threads.emplace_back(worker); for (auto& i : threads) i.join(); diff --git a/src/search/setup.cpp b/src/search/setup.cpp index e997cfdd9..9aa332057 100644 --- a/src/search/setup.cpp +++ b/src/search/setup.cpp @@ -26,37 +26,71 @@ along with this program. If not, see . #include "search.h" #include "hit.h" +using std::vector; using std::endl; using std::map; +using std::string; +using std::prev; +using std::max; namespace Search { const double SINGLE_INDEXED_SEED_SPACE_MAX_COVERAGE = 0.15; -const map sensitivity_traits { - // qidx motifm freqsd minid ug_ev ug_ev_s gf_ev idx_chunk qbins ctg_seed seed_cut - { Sensitivity::FAST, {true, true, 50.0, 11, 10000, 10000, 0, 4, 16, nullptr, 0.9 }}, - { Sensitivity::DEFAULT, {true, true, 50.0, 11, 10000, 10000, 0, 4, 16, "111111", 0.8 }}, - { Sensitivity::MID_SENSITIVE, {true, true, 20.0, 11, 10000, 10000, 0, 4, 16, nullptr, 1.0 }}, - { Sensitivity::SENSITIVE, {true, true, 20.0, 11, 10000, 10000, 1, 4, 16, "11111", 1.0 }}, - { Sensitivity::MORE_SENSITIVE, {true, false, 200.0, 11, 10000, 10000, 1, 4, 16, "11111", 1.0 }}, - { Sensitivity::VERY_SENSITIVE, {true, false, 15.0, 9, 100000, 30000, 1, 1, 16, nullptr, 1.0 }}, - { Sensitivity::ULTRA_SENSITIVE, {true, false, 20.0, 9, 300000, 30000, 1, 1, 64, nullptr, 1.0 }} -}; +Reduction murphy10("A KR EDNQ C G H ILVM FYW P ST"); +Reduction steinegger12("AST C DN EQ FY G H IV KR LM P W"); +Reduction dna("A C G T"); + +const map sensitivity_traits[2] = { + // qidx motifm freqsd minid ug_ev ug_ev_s gf_ev idx_chunk qbins ctg_seed seed_cut block_size reduction min_window + {{ Sensitivity::FASTER, {true, true, 50.0, 11, 0, 0, 0, 4, 16, nullptr, 0.9, 2.0, murphy10, 12 }}, + { Sensitivity::FAST, {true, true, 50.0, 11, 0, 0, 0, 4, 16, nullptr, 0.9, 2.0, murphy10, 0 }}, + { Sensitivity::DEFAULT, {true, true, 50.0, 11, 10000, 10000, 0, 4, 16, "111111", 0.8, 2.0, murphy10, 0 }}, + { Sensitivity::MID_SENSITIVE, {true, true, 20.0, 11, 10000, 10000, 0, 4, 16, nullptr, 1.0, 2.0, murphy10, 0 }}, + { Sensitivity::SENSITIVE, {true, true, 20.0, 11, 10000, 10000, 1, 4, 16, "11111", 1.0, 2.0, murphy10, 0 }}, + { Sensitivity::MORE_SENSITIVE, {true, false, 200.0, 11, 10000, 10000, 1, 4, 16, "11111", 1.0, 2.0, murphy10, 0 }}, + { Sensitivity::VERY_SENSITIVE, {true, false, 15.0, 9, 100000, 30000, 1, 1, 16, nullptr, 1.0, 0.4, murphy10, 0 }}, + { Sensitivity::ULTRA_SENSITIVE, {true, false, 20.0, 9, 300000, 30000, 1, 1, 64, nullptr, 1.0, 0.4, murphy10, 0 }}, +}, +{{Sensitivity::DEFAULT, {true, false, 20.0, 9, 0, 0, 0, 4, 16, nullptr, 1.0, 2.0, dna, 12 }} +}}; + const map> iterated_sens{ + { Sensitivity::FASTER, { }}, { Sensitivity::FAST, { }}, - { Sensitivity::DEFAULT, { }}, - { Sensitivity::MID_SENSITIVE, {Sensitivity::DEFAULT}}, - { Sensitivity::SENSITIVE, {Sensitivity::DEFAULT}}, - { Sensitivity::MORE_SENSITIVE, {Sensitivity::DEFAULT}}, - { Sensitivity::VERY_SENSITIVE, {Sensitivity::DEFAULT, Sensitivity::MORE_SENSITIVE}}, - { Sensitivity::ULTRA_SENSITIVE, {Sensitivity::DEFAULT, Sensitivity::MORE_SENSITIVE}} + { Sensitivity::DEFAULT, {Sensitivity::FAST }}, + { Sensitivity::MID_SENSITIVE, {Sensitivity::FAST, Sensitivity::DEFAULT}}, + { Sensitivity::SENSITIVE, {Sensitivity::FAST, Sensitivity::DEFAULT}}, + { Sensitivity::MORE_SENSITIVE, {Sensitivity::FAST, Sensitivity::DEFAULT}}, + { Sensitivity::VERY_SENSITIVE, {Sensitivity::FAST, Sensitivity::DEFAULT, Sensitivity::MORE_SENSITIVE}}, + { Sensitivity::ULTRA_SENSITIVE, {Sensitivity::FAST, Sensitivity::DEFAULT, Sensitivity::MORE_SENSITIVE}} }; -const map> shape_codes = { +const map> cluster_sens{ + { Sensitivity::FASTER, { }}, + { Sensitivity::FAST, { }}, + { Sensitivity::DEFAULT, {Sensitivity::FAST }}, + { Sensitivity::MID_SENSITIVE, {Sensitivity::FAST }}, + { Sensitivity::SENSITIVE, {Sensitivity::FAST, Sensitivity::DEFAULT }}, + { Sensitivity::MORE_SENSITIVE, {Sensitivity::FAST, Sensitivity::DEFAULT }}, + { Sensitivity::VERY_SENSITIVE, {Sensitivity::FAST, Sensitivity::DEFAULT, Sensitivity::MORE_SENSITIVE}}, + { Sensitivity::ULTRA_SENSITIVE, {Sensitivity::FAST, Sensitivity::DEFAULT, Sensitivity::MORE_SENSITIVE}} +}; - {Sensitivity::DEFAULT, { +const map approx_id_to_hamming_id { + { 50.0, 20 }, + { 90.0, 30 } +}; + +static unsigned hamming_id_cutoff(double approx_id) { + const auto it = approx_id_to_hamming_id.upper_bound(approx_id); + return it == approx_id_to_hamming_id.begin() ? 0 : prev(it)->second; +} + +const map> shape_codes[2] ={ + + {{Sensitivity::DEFAULT, { "111101110111", "111011010010111" }}, // 2x10 iedera {Sensitivity::SENSITIVE, { @@ -186,10 +220,16 @@ const map> shape_codes = { "1110011000011011" }}, // 8x9 { Sensitivity::FAST, - { "1101110101101111" } } + { "1101110101101111" } }, + { Sensitivity::FASTER, + { "1101110101101111" } }}, + { + { Sensitivity::DEFAULT, + { "1111111111111111" } }} }; + bool use_single_indexed(double coverage, size_t query_letters, size_t ref_letters) { if (coverage >= SINGLE_INDEXED_SEED_SPACE_MAX_COVERAGE) @@ -219,14 +259,15 @@ MaskingAlgo soft_masking_algo(const SensitivityTraits& traits) { void setup_search(Sensitivity sens, Search::Config& cfg) { - const SensitivityTraits& traits = sensitivity_traits.at(sens); + const SensitivityTraits& traits = sensitivity_traits[(int)align_mode.sequence_type].at(sens); config.sensitivity = sens; ::Config::set_option(cfg.freq_sd, config.freq_sd_, 0.0, traits.freq_sd); - ::Config::set_option(cfg.hamming_filter_id, config.min_identities_, 0u, traits.min_identities); + ::Config::set_option(cfg.hamming_filter_id, config.min_identities_, 0u, max(traits.min_identities, hamming_id_cutoff(config.approx_min_id.get(0.0)))); ::Config::set_option(cfg.ungapped_evalue, config.ungapped_evalue_, -1.0, traits.ungapped_evalue); ::Config::set_option(cfg.ungapped_evalue_short, config.ungapped_evalue_short_, -1.0, traits.ungapped_evalue_short); ::Config::set_option(cfg.gapped_filter_evalue, config.gapped_filter_evalue_, -1.0, traits.gapped_filter_evalue); ::Config::set_option(cfg.query_bins, config.query_bins_, 0u, traits.query_bins); + ::Config::set_option(cfg.minimizer_window, config.minimizer_window_, 0, traits.minimizer_window); if (config.algo == ::Config::Algo::CTG_SEED) { if (!traits.contiguous_seed) @@ -236,12 +277,16 @@ void setup_search(Sensitivity sens, Search::Config& cfg) ::shapes = ShapeConfig({ traits.contiguous_seed }, 0); } else - ::shapes = ShapeConfig(config.shape_mask.empty() ? shape_codes.at(sens) : config.shape_mask, config.shapes); + ::shapes = ShapeConfig(config.shape_mask.empty() ? shape_codes[(int)align_mode.sequence_type].at(sens) : config.shape_mask, config.shapes); config.gapped_filter_diag_score = score_matrix.rawscore(config.gapped_filter_diag_bit_score); const double seed_cut = config.seed_cut_ == 0.0 ? traits.seed_cut : config.seed_cut_; cfg.seed_complexity_cut = seed_cut * std::log(2.0) * ::shapes[0].weight_; cfg.soft_masking = soft_masking_algo(traits); + if (!config.soft_masking.empty()) + cfg.soft_masking |= from_string(config.soft_masking); + cfg.cutoff_table = { cfg.ungapped_evalue }; + cfg.cutoff_table_short = { cfg.ungapped_evalue_short }; } } \ No newline at end of file diff --git a/src/search/sse_dist.h b/src/search/sse_dist.h index ae43825f9..6d9e3434f 100644 --- a/src/search/sse_dist.h +++ b/src/search/sse_dist.h @@ -34,9 +34,9 @@ static inline __m128i reduce_seq_ssse3(const Letter *seq, const Letter* map) #ifdef SEQ_MASK s = letter_mask(s); #endif - __m128i high_mask = _mm_slli_epi16(_mm_and_si128(s, ::SIMD::_mm_set1_epi8('\x10')), 3); + __m128i high_mask = _mm_slli_epi16(_mm_and_si128(s, _mm_set1_epi8('\x10')), 3); __m128i seq_low = _mm_or_si128(s, high_mask); - __m128i seq_high = _mm_or_si128(s, _mm_xor_si128(high_mask, ::SIMD::_mm_set1_epi8('\x80'))); + __m128i seq_high = _mm_or_si128(s, _mm_xor_si128(high_mask, _mm_set1_epi8('\x80'))); __m128i r1 = _mm_loadu_si128(row); __m128i r2 = _mm_loadu_si128(row+1); @@ -117,7 +117,7 @@ static inline uint64_t seed_mask(const Letter* s, int len) { #ifdef __SSE2__ assert(len <= 64); uint64_t mask = 0; - const __m128i m = ::SIMD::_mm_set1_epi8(SEED_MASK); + const __m128i m = _mm_set1_epi8(SEED_MASK); for (int i = 0; i < len; i += 16) { const __m128i mask_bits = _mm_and_si128(_mm_loadu_si128((const __m128i*)s), m); mask |= (uint64_t)_mm_movemask_epi8(mask_bits) << i; diff --git a/src/search/stage0.cpp b/src/search/stage0.cpp index d6a5a147c..fa6c11fde 100644 --- a/src/search/stage0.cpp +++ b/src/search/stage0.cpp @@ -53,7 +53,7 @@ static void seed_join_worker( DoubleArray *ref_seeds_hits) { int p; - const unsigned bits = query_seeds->key_bits; + const int bits = query_seeds->key_bits; if (bits != ref_seeds->key_bits) throw std::runtime_error("Joining seed arrays with different key lengths."); while ((p = (*seedp)++) < seedp_range->end()) { @@ -74,18 +74,25 @@ static void search_worker(atomic *seedp, const SeedPartitionRange *see else writer.reset(new AsyncBuffer::Iterator(*cfg->seed_hit_buf, thread_id)); #ifdef __APPLE__ - unique_ptr work_set(new Search::WorkSet{ *context, *cfg, shape, {}, writer.get(), {} }); + unique_ptr work_set(new Search::WorkSet{ *context, *cfg, shape, {}, writer.get(), {}, context->kmer_ranking }); #else - unique_ptr work_set(new Search::WorkSet{ *context, *cfg, shape, {}, writer.get(), {}, {}, {} }); + unique_ptr work_set(new Search::WorkSet{ *context, *cfg, shape, {}, writer.get(), {}, {}, {}, context->kmer_ranking }); #endif int p; +#ifdef KEEP_TARGET_ID + auto f = config.lin_stage1 ? Search::stage1_lin_ranked + : (config.self && cfg->current_ref_block == 0 ? Search::stage1_self : Search::stage1); +#else + auto f = config.lin_stage1 ? Search::stage1_lin + : (config.self && cfg->current_ref_block == 0 ? Search::stage1_self : Search::stage1); +#endif while ((p = (*seedp)++) < seedp_range->end()) for (auto it = JoinIterator(query_seed_hits[p].begin(), ref_seed_hits[p].begin()); it; ++it) - Search::stage1(it.r->begin(), it.r->size(), it.s->begin(), it.s->size(), *work_set); + f(it.r->begin(), (int32_t)it.r->size(), it.s->begin(), (int32_t)it.s->size(), *work_set); statistics += work_set->stats; } -void search_shape(unsigned sid, unsigned query_block, unsigned query_iteration, char *query_buffer, char *ref_buffer, Search::Config& cfg, const HashedSeedSet* target_seeds) +void search_shape(unsigned sid, int query_block, unsigned query_iteration, char *query_buffer, char *ref_buffer, Search::Config& cfg, const HashedSeedSet* target_seeds) { Partition p(Const::seedp, cfg.index_chunks); DoubleArray query_seed_hits[Const::seedp], ref_seed_hits[Const::seedp]; @@ -97,7 +104,7 @@ void search_shape(unsigned sid, unsigned query_block, unsigned query_iteration, message_stream << "Processing query block " << query_block + 1; if (cfg.iterated()) message_stream << ", query iteration " << query_iteration + 1; - message_stream << ", reference block " << (current_ref_block + 1) << "/" << cfg.ref_blocks + message_stream << ", reference block " << (cfg.current_ref_block + 1) << "/" << cfg.ref_blocks << ", shape " << (sid + 1) << "/" << shapes.count(); if (cfg.index_chunks > 1) message_stream << ", index chunk " << chunk + 1 << "/" << cfg.index_chunks; @@ -108,7 +115,8 @@ void search_shape(unsigned sid, unsigned query_block, unsigned query_iteration, task_timer timer("Building reference seed array", true); SeedArray *ref_idx; const EnumCfg enum_ref{ &ref_hst.partition(), sid, sid + 1, cfg.seed_encoding, nullptr, false, false, cfg.seed_complexity_cut, - query_seeds_bitset.get() || query_seeds_hashed.get() ? MaskingAlgo::NONE : cfg.soft_masking }; + query_seeds_bitset.get() || query_seeds_hashed.get() ? MaskingAlgo::NONE : cfg.soft_masking, + cfg.minimizer_window }; if (query_seeds_bitset.get()) ref_idx = new SeedArray(*cfg.target, ref_hst.get(sid), range, ref_buffer, query_seeds_bitset.get(), enum_ref); else if (query_seeds_hashed.get()) @@ -119,7 +127,7 @@ void search_shape(unsigned sid, unsigned query_block, unsigned query_iteration, timer.go("Building query seed array"); SeedArray* query_idx; - EnumCfg enum_query{ target_seeds ? nullptr : &query_hst.partition(), sid, sid + 1, cfg.seed_encoding, cfg.query_skip.get(), false, true, cfg.seed_complexity_cut, cfg.soft_masking }; + EnumCfg enum_query{ target_seeds ? nullptr : &query_hst.partition(), sid, sid + 1, cfg.seed_encoding, cfg.query_skip.get(), false, true, cfg.seed_complexity_cut, cfg.soft_masking, cfg.minimizer_window }; if (target_seeds) query_idx = new SeedArray(*cfg.query, range, target_seeds, enum_query); else @@ -136,39 +144,49 @@ void search_shape(unsigned sid, unsigned query_block, unsigned query_iteration, timer.go("Computing hash join"); atomic seedp(range.begin()); vector threads; - for (size_t i = 0; i < config.threads_; ++i) + for (int i = 0; i < config.threads_; ++i) threads.emplace_back(seed_join_worker, query_idx, ref_idx, &seedp, &range, query_seed_hits, ref_seed_hits); for (auto &t : threads) t.join(); timer.finish(); - if(config.freq_masking) { + if(config.freq_masking && !config.lin_stage1) { timer.go("Building seed filter"); frequent_seeds.build(sid, range, query_seed_hits, ref_seed_hits, cfg); } else Search::mask_seeds(shapes[sid], range, query_seed_hits, ref_seed_hits, cfg); + unique_ptr kmer_ranking; +#ifdef KEEP_TARGET_ID + if (config.lin_stage1) { + timer.go("Building kmer ranking"); + kmer_ranking.reset(config.kmer_ranking ? new KmerRanking(cfg.query->seqs(), query_seed_hits, ref_seed_hits) + : new KmerRanking(cfg.query->seqs())); + } +#endif + Search::Context* context = nullptr; const vector patterns = shapes.patterns(0, sid + 1); context = new Search::Context{ {patterns.data(), patterns.data() + patterns.size() - 1 }, {patterns.data(), patterns.data() + patterns.size() }, - cfg.ungapped_evalue, - cfg.ungapped_evalue_short, - score_matrix.rawscore(config.short_query_ungapped_bitscore) + score_matrix.rawscore(config.short_query_ungapped_bitscore), + kmer_ranking.get() }; timer.go("Searching alignments"); seedp = range.begin(); threads.clear(); - for (size_t i = 0; i < config.threads_; ++i) + for (int i = 0; i < config.threads_; ++i) threads.emplace_back(search_worker, &seedp, &range, sid, i, query_seed_hits, ref_seed_hits, context, &cfg); for (auto &t : threads) t.join(); + timer.go("Deallocating memory"); delete ref_idx; delete query_idx; delete context; + kmer_ranking.reset(); } } diff --git a/src/search/stage2.cpp b/src/search/stage2.cpp index 9101d7b3d..c82aafdb4 100644 --- a/src/search/stage2.cpp +++ b/src/search/stage2.cpp @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2020-2022 Max Planck Society for the Advancement of Science e.V. Code developed by Benjamin Buchfink @@ -35,22 +35,25 @@ along with this program. If not, see . #include "../run/config.h" using std::vector; +using std::numeric_limits; namespace Search { namespace DISPATCH_ARCH { static const int SHORT_QUERY_LEN = 85; -static int ungapped_cutoff(int query_len, const Context& context) { +static int ungapped_cutoff(int query_len, const WorkSet& context) { #ifdef UNGAPPED_SPOUGE return query_len > config.short_query_max_len ? context.cutoff_table(query_len, 50) : context.short_query_ungapped_cutoff; #else - if (query_len <= config.short_query_max_len) - return context.short_query_ungapped_cutoff; + if (context.cfg.ungapped_evalue == 0.0) + return 0; + else if (query_len <= config.short_query_max_len) + return context.context.short_query_ungapped_cutoff; else if (query_len <= SHORT_QUERY_LEN && align_mode.query_translated) - return context.cutoff_table_short(query_len); + return context.cfg.cutoff_table_short(query_len); else - return context.cutoff_table(query_len); + return context.cfg.cutoff_table(query_len); #endif } @@ -63,11 +66,11 @@ static int ungapped_window(int query_len) { static void search_query_offset(const SeedLoc& q, const SeedLoc* s, - FlatArray::ConstIterator hits, - FlatArray::ConstIterator hits_end, + FlatArray::DataConstIterator hits, + FlatArray::DataConstIterator hits_end, WorkSet& work_set) { - constexpr auto N = vector::const_iterator::difference_type(::DISPATCH_ARCH::SIMD::Vector::CHANNELS); + constexpr int N = ::DISPATCH_ARCH::SIMD::Vector::CHANNELS; const SequenceSet& ref_seqs = work_set.cfg.target->seqs(), &query_seqs = work_set.cfg.query->seqs(); const Letter* query = query_seqs.data(q); @@ -75,40 +78,43 @@ static void search_query_offset(const SeedLoc& q, int scores[N]; std::fill(scores, scores + N, INT_MAX); - unsigned query_id = UINT_MAX, seed_offset = UINT_MAX; + unsigned query_id = UINT_MAX; + Loc seed_offset = numeric_limits::max(); #ifdef KEEP_TARGET_ID query_id = q.block_id; - seed_offset = (size_t)q.pos - query_seqs.position(query_id, 0); + seed_offset = Loc((int64_t)q.pos - query_seqs.position(query_id, 0)); #else std::pair l = query_seqs.local_position((uint64_t)q); // lazy eval? query_id = (unsigned)l.first; seed_offset = (unsigned)l.second; #endif const int query_len = query_seqs.length(query_id); - const int score_cutoff = ungapped_cutoff(query_len, work_set.context); + const int score_cutoff = ungapped_cutoff(query_len, work_set); const int window = ungapped_window(query_len); const Sequence query_clipped = Util::Seq::clip(query - window, window * 2, window); const int window_left = int(query - query_clipped.data()), window_clipped = (int)query_clipped.length(); const unsigned sid = work_set.shape_id; const bool chunked = work_set.cfg.index_chunks > 1; const unsigned hamming_filter_id = work_set.cfg.hamming_filter_id; - size_t n = 0, hit_count = 0; + int n = 0; + size_t hit_count = 0; const int interval_mod = config.left_most_interval > 0 ? seed_offset % config.left_most_interval : window_left, interval_overhang = std::max(window_left - interval_mod, 0); - for (FlatArray::ConstIterator i = hits; i < hits_end; i += n) { + for (FlatArray::DataConstIterator i = hits; i < hits_end; i += n) { - n = std::min(N, hits_end - i); - for (size_t j = 0; j < n; ++j) + n = std::min(N, int(hits_end - i)); + for (int j = 0; j < n; ++j) subjects[j] = ref_seqs.data(s[*(i + j)]) - window_left; - DP::window_ungapped_best(query_clipped.data(), subjects, n, window_clipped, scores); + if (score_cutoff) + DP::window_ungapped_best(query_clipped.data(), subjects, n, window_clipped, scores); if (config.global_ranking_targets) - for (size_t j = 0; j < n; ++j) + for (int j = 0; j < n; ++j) if (scores[j] == UCHAR_MAX) scores[j] = ::ungapped_window(query_clipped.data(), subjects[j], window_clipped); - for (size_t j = 0; j < n; ++j) { + for (int j = 0; j < n; ++j) { if (scores[j] > score_cutoff) { #ifdef UNGAPPED_SPOUGE std::pair l = ref_seqs::data_->local_position(s[*(i + j)]); @@ -116,13 +122,15 @@ static void search_query_offset(const SeedLoc& q, continue; #endif work_set.stats.inc(Statistics::TENTATIVE_MATCHES2); - if (left_most_filter(query_clipped + interval_overhang, subjects[j] + interval_overhang, window_left - interval_overhang, shapes[sid].length_, work_set.context, sid == 0, sid, score_cutoff, chunked, hamming_filter_id)) { + if (work_set.cfg.minimizer_window || config.sketch_size + || left_most_filter(query_clipped + interval_overhang, subjects[j] + interval_overhang, window_left - interval_overhang, shapes[sid].length_, work_set.context, sid == 0, sid, score_cutoff, chunked, hamming_filter_id)) { work_set.stats.inc(Statistics::TENTATIVE_MATCHES3); if (hit_count++ == 0) *work_set.out = SerializerTraits::make_sentry(query_id, seed_offset); #ifdef KEEP_TARGET_ID if(config.global_ranking_targets) - *work_set.out = { query_id, (uint64_t)s[*(i + j)].block_id, seed_offset, (uint16_t)scores[j] }; + //*work_set.out = { query_id, (uint64_t)s[*(i + j)].block_id, seed_offset, (uint16_t)scores[j] }; + *work_set.out = { query_id, (uint64_t)s[*(i + j)].pos, seed_offset, (uint16_t)scores[j], s[*(i + j)].block_id }; else #ifdef HIT_KEEP_TARGET_ID *work_set.out = { query_id, (uint64_t)s[*(i + j)].pos, seed_offset, (uint16_t)scores[j], s[*(i + j)].block_id }; @@ -140,8 +148,8 @@ static void search_query_offset(const SeedLoc& q, static void FLATTEN search_tile( const FlatArray &hits, - uint32_t query_begin, - uint32_t subject_begin, + int32_t query_begin, + int32_t subject_begin, const SeedLoc* q, const SeedLoc* s, WorkSet& work_set) @@ -150,7 +158,7 @@ static void FLATTEN search_tile( const uint32_t query_count = (uint32_t)hits.size(); const SeedLoc* q_begin = q + query_begin, *s_begin = s + subject_begin; for (uint32_t i = 0; i < query_count; ++i) { - FlatArray::ConstIterator r1 = hits.begin(i), r2 = hits.end(i); + FlatArray::DataConstIterator r1 = hits.begin(i), r2 = hits.end(i); if (r2 == r1) continue; search_query_offset(q_begin[i], s_begin, r1, r2, work_set); @@ -169,35 +177,114 @@ static void all_vs_all(const FingerPrint* a, uint32_t na, const FingerPrint* b, } } +static void all_vs_all_self(const FingerPrint* a, uint32_t na, FlatArray& out, unsigned hamming_filter_id) { + for (uint32_t i = 0; i < na; ++i) { + const FingerPrint e = a[i]; + out.next(); + for (uint32_t j = i + 1; j < na; ++j) + if (e.match(a[j]) >= hamming_filter_id) + out.push_back(j); + } +} + static void load_fps(const SeedLoc* p, size_t n, Container& v, const SequenceSet& seqs) { v.clear(); v.reserve(n); const SeedLoc* end = p + n; - for (; p < end; ++p) + for (; p < end; ++p) { v.emplace_back(seqs.data(*p)); + } } -void FLATTEN stage1(const SeedLoc* q, size_t nq, const SeedLoc* s, size_t ns, WorkSet& work_set) +void FLATTEN stage1(const SeedLoc* q, int32_t nq, const SeedLoc* s, int32_t ns, WorkSet& work_set) { #ifdef __APPLE__ thread_local Container vq, vs; #else Container& vq = work_set.vq, &vs = work_set.vs; #endif + + const int32_t tile_size = config.tile_size; + load_fps(s, ns, vs, work_set.cfg.target->seqs()); work_set.stats.inc(Statistics::SEED_HITS, nq * ns); load_fps(q, nq, vq, work_set.cfg.query->seqs()); - load_fps(s, ns, vs, work_set.cfg.target->seqs()); - - const size_t tile_size = config.tile_size; - for (size_t i = 0; i < vq.size(); i += tile_size) { - for (size_t j = 0; j < vs.size(); j += tile_size) { + const int32_t qs = (int32_t)vq.size(), ss = (int32_t)vs.size(); + for (int32_t i = 0; i < qs; i += tile_size) { + for (int32_t j = 0; j < ss; j += tile_size) { work_set.hits.clear(); - all_vs_all(vq.data() + i, (uint32_t)std::min(tile_size, vq.size() - i), vs.data() + j, (uint32_t)std::min(tile_size, vs.size() - j), work_set.hits, work_set.cfg.hamming_filter_id); + all_vs_all(vq.data() + i, std::min(tile_size, qs - i), vs.data() + j, std::min(tile_size, ss - j), work_set.hits, work_set.cfg.hamming_filter_id); search_tile(work_set.hits, i, j, q, s, work_set); } } +} + +void FLATTEN stage1_lin(const SeedLoc* q, int32_t nq, const SeedLoc* s, int32_t ns, WorkSet& work_set) +{ +#ifdef __APPLE__ + thread_local Container vq, vs; +#else + Container& vq = work_set.vq, &vs = work_set.vs; +#endif + + const int32_t tile_size = config.tile_size; + load_fps(q, 1, vq, work_set.cfg.query->seqs()); + load_fps(s, ns, vs, work_set.cfg.target->seqs()); + work_set.stats.inc(Statistics::SEED_HITS, ns); + + const int32_t ss = (int32_t)vs.size(); + for (int32_t j = 0; j < ss; j += tile_size) { + work_set.hits.clear(); + all_vs_all(vq.data(), 1, vs.data() + j, std::min(tile_size, ss - j), work_set.hits, work_set.cfg.hamming_filter_id); + search_tile(work_set.hits, 0, j, q, s, work_set); + } +} + +void FLATTEN stage1_lin_ranked(const SeedLoc* q, int32_t nq, const SeedLoc* s, int32_t ns, WorkSet& work_set) +{ +#ifdef __APPLE__ + thread_local Container vq, vs; +#else + Container& vq = work_set.vq, &vs = work_set.vs; +#endif + + const int32_t tile_size = config.tile_size; + const int32_t ranking = work_set.kmer_ranking->highest_ranking(q, q + nq); + load_fps(q + ranking, 1, vq, work_set.cfg.query->seqs()); + load_fps(s, ns, vs, work_set.cfg.target->seqs()); + work_set.stats.inc(Statistics::SEED_HITS, ns); + const int32_t ss = (int32_t)vs.size(); + for (int32_t j = 0; j < ss; j += tile_size) { + work_set.hits.clear(); + all_vs_all(vq.data(), 1, vs.data() + j, std::min(tile_size, ss - j), work_set.hits, work_set.cfg.hamming_filter_id); + search_tile(work_set.hits, ranking, j, q, s, work_set); + } +} + +void FLATTEN stage1_self(const SeedLoc* q, int32_t nq, const SeedLoc* s, int32_t ns, WorkSet& work_set) +{ +#ifdef __APPLE__ + thread_local Container vs; +#else + Container& vs = work_set.vs; +#endif + + const int32_t tile_size = config.tile_size; + load_fps(s, ns, vs, work_set.cfg.target->seqs()); + + work_set.stats.inc(Statistics::SEED_HITS, ns*(ns - 1) / 2); + const int32_t ss = (int32_t)vs.size(); + for (int32_t i = 0; i < ss; i += tile_size) { + work_set.hits.clear(); + all_vs_all_self(vs.data() + i, std::min(tile_size, ss - i), work_set.hits, work_set.cfg.hamming_filter_id); + search_tile(work_set.hits, i, i, s, s, work_set); + for (int32_t j = i + tile_size; j < ss; j += tile_size) { + work_set.hits.clear(); + all_vs_all(vs.data() + i, std::min(tile_size, ss - i), vs.data() + j, std::min(tile_size, ss - j), work_set.hits, work_set.cfg.hamming_filter_id); + search_tile(work_set.hits, i, j, s, s, work_set); + } + } } }} \ No newline at end of file diff --git a/src/stats/cbs.cpp b/src/stats/cbs.cpp index bfdb8c188..c52cb24f8 100644 --- a/src/stats/cbs.cpp +++ b/src/stats/cbs.cpp @@ -23,6 +23,8 @@ along with this program. If not, see . #include "score_matrix.h" #include "../masking/masking.h" +using std::vector; + namespace Stats { CBS comp_based_stats(0, -1.0, -1.0, -1.0); @@ -147,7 +149,7 @@ TargetMatrix::TargetMatrix(const int16_t* query_matrix, const int16_t* target_ma for (size_t i = 0; i < AMINO_ACID_COUNT; ++i) { for (size_t j = 0; j < AMINO_ACID_COUNT; ++j) if (i < 20 && j < 20) { - const int score = std::round(((double)query_matrix[i * 20 + j] + (double)target_matrix[i * 20 + j]) * f / 2.0); + const int score = (int)std::round(((double)query_matrix[i * 20 + j] + (double)target_matrix[i * 20 + j]) * f / 2.0); scores[i * 32 + j] = score; scores32[i * 32 + j] = score; score_min = std::min(score_min, score); diff --git a/src/stats/cbs.h b/src/stats/cbs.h index 69b9fb872..880a21985 100644 --- a/src/stats/cbs.h +++ b/src/stats/cbs.h @@ -130,7 +130,7 @@ struct CBS { case DISABLED: case HAUSER: return false; - case HAUSER_AND_AVG_MATRIX_ADJUST: + case DEPRECATED1: case HAUSER_AND_MATRIX_ADJUST: case MATRIX_ADJUST: case COMP_BASED_STATS: @@ -150,17 +150,9 @@ struct CBS { return false; } } - static bool avg_matrix(unsigned code) { - switch (code) { - case HAUSER_AND_AVG_MATRIX_ADJUST: - return true; - default: - return false; - } - } static bool conditioned(unsigned code) { switch (code) { - case HAUSER_AND_AVG_MATRIX_ADJUST: + case DEPRECATED1: case HAUSER_AND_MATRIX_ADJUST: case COMP_BASED_STATS_AND_MATRIX_ADJUST: return true; @@ -179,7 +171,7 @@ struct CBS { } static int target_seg(unsigned code) { switch (code) { - case HAUSER_AND_AVG_MATRIX_ADJUST: + case DEPRECATED1: case HAUSER_AND_MATRIX_ADJUST: case MATRIX_ADJUST: case COMP_BASED_STATS: @@ -193,7 +185,7 @@ struct CBS { enum { DISABLED = 0, HAUSER = 1, - HAUSER_AND_AVG_MATRIX_ADJUST = 2, + DEPRECATED1 = 2, HAUSER_AND_MATRIX_ADJUST = 3, MATRIX_ADJUST = 4, COMP_BASED_STATS = 5, diff --git a/src/stats/dna_scoring/build_score.cpp b/src/stats/dna_scoring/build_score.cpp new file mode 100644 index 000000000..9b19fe1bb --- /dev/null +++ b/src/stats/dna_scoring/build_score.cpp @@ -0,0 +1,116 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Dimitrios Koutsogiannis + +Code developed by Dimitrios Koutsogiannis + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#include +#include "../lib/blast/blast_stat.h" +#include "../lib/blast/blast_encoding.h" +#include "../lib/blast/blast_setup.h" +#include "build_score.h" + + +namespace Stats { + +Blastn_Score::Blastn_Score(const int reward, const int penalty, const int gapopen, const int gapextend,uint64_t db_letters,int64_t sequence_count) : + m_ScoreBlk(BlastScoreBlkNew(BLASTNA_SEQ_CODE, 1)), + reward_(reward), + penalty_(penalty), + gap_open_(gapopen), + gap_extend_(gapextend), + target_length_(db_letters), + db_size_(sequence_count) + +{ + int status; + + + + if(m_ScoreBlk == nullptr) + throw std::runtime_error("Failed to initialize blast score block"); + + + m_ScoreBlk->kbp_gap_std[0] = Blast_KarlinBlkNew(); + Blast_ScoreBlkKbpIdealCalc(m_ScoreBlk); + m_ScoreBlk->reward = reward_; + m_ScoreBlk->penalty = penalty_; + + EBlastProgramType core_type =eBlastTypeBlastn; + BlastScoringOptions *score_options; + BlastScoringOptionsNew(core_type, &score_options); + BLAST_FillScoringOptions(score_options, core_type, TRUE,penalty_, + reward_, + "", + gap_open_, gap_extend_); + status = Blast_ScoreBlkMatrixInit(core_type, score_options, + m_ScoreBlk, nullptr); + score_options = BlastScoringOptionsFree(score_options); + if (status) + throw std::runtime_error("Failed to initialize scoring matrix"); + + m_ScoreBlk->kbp_gap_std[0] = Blast_KarlinBlkNew(); + + Blast_ScoreBlkKbpIdealCalc(m_ScoreBlk); + status = Blast_KarlinBlkNuclGappedCalc(m_ScoreBlk->kbp_gap_std[0], + gap_open_, gap_extend_, + m_ScoreBlk->reward, + m_ScoreBlk->penalty, + m_ScoreBlk->kbp_ideal, + &(m_ScoreBlk->round_down), + nullptr); + + + if (status || m_ScoreBlk->kbp_gap_std[0] == nullptr || + m_ScoreBlk->kbp_gap_std[0]->Lambda <= 0.0) { + throw std::runtime_error("Failed to initialize Karlin Blocks"); + } + + kbp = m_ScoreBlk->kbp_gap_std[0] ; +} + +double Blastn_Score::blast_bit_Score(int raw_score) const { + + return ((raw_score * kbp->Lambda) - kbp->logK) / NCBIMATH_LN2; + } + + +double Blastn_Score::blast_eValue(int raw_score,int query_length) const { + uint64_t searchspace = calculate_length_adjustment(query_length,query_length,this->target_length_) *calculate_length_adjustment(this->target_length_, query_length,this->target_length_,this->db_size_); + return BLAST_KarlinStoE_simple(raw_score, kbp,(long)searchspace); + } + +uint64_t Blastn_Score::calculate_length_adjustment(uint64_t length, int query_length, uint64_t target_length, int64_t db_size) const { + return length - (uint64_t)(expected_hsp_value(query_length,target_length) * (double)db_size); +} + +uint64_t Blastn_Score::calculate_length_adjustment(uint64_t length, int query_length, uint64_t target_length) const { + if(expected_hsp_value(query_length,target_length) < (1 / kbp->K)) + return (int64_t) (1/kbp->K); + else + return length - (int64_t) expected_hsp_value(query_length,target_length); +} + +double Blastn_Score::expected_hsp_value(int query_length, uint64_t target_length) const { + return (log(kbp->K * query_length * (double)target_length) / kbp->H); +} + +Blastn_Score::~Blastn_Score() { + m_ScoreBlk = BlastScoreBlkFree(m_ScoreBlk); +} + +} \ No newline at end of file diff --git a/src/stats/dna_scoring/build_score.h b/src/stats/dna_scoring/build_score.h new file mode 100644 index 000000000..67f715931 --- /dev/null +++ b/src/stats/dna_scoring/build_score.h @@ -0,0 +1,45 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Dimitrios Koutsogiannis + +Code developed by Dimitrios Koutsogiannis + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#include "blast/blast_stat.h" + +namespace Stats { +struct Blastn_Score{ + Blastn_Score(const int reward,const int penalty,const int gapopen,const int gapextend,uint64_t db_letters,int64_t sequence_count); + double blast_bit_Score(int raw_score) const; + double blast_eValue(int raw_score,int query_length) const; + ~Blastn_Score(); + int reward()const{return reward_;} + int penalty()const{return penalty_;} + int gap_open()const{return gap_open_;} + int gap_extend()const{return gap_extend_;} + + +private: + Blast_KarlinBlk* kbp; + BlastScoreBlk *m_ScoreBlk; + int reward_,penalty_,gap_open_,gap_extend_; + uint64_t target_length_; + int64_t db_size_; + uint64_t calculate_length_adjustment(uint64_t length,int query_length, uint64_t target_length, int64_t db_size) const; + uint64_t calculate_length_adjustment(uint64_t length,int query_length, uint64_t target_length) const; + double expected_hsp_value(int query_length, uint64_t target_length) const; +}; +} diff --git a/src/stats/hauser_correction.cpp b/src/stats/hauser_correction.cpp index 91cb93b56..03b0bab2b 100644 --- a/src/stats/hauser_correction.cpp +++ b/src/stats/hauser_correction.cpp @@ -25,6 +25,9 @@ along with this program. If not, see . #include "score_matrix.h" using std::array; +using std::vector; + +static const int PADDING = 32; struct Vector_scores { @@ -99,9 +102,10 @@ Bias_correction::Bias_correction(const Sequence &seq): ++m; } - int8.reserve(seq.length()); + int8.reserve(seq.length() + PADDING); for (float f : *this) int8.push_back(int8_t(f < 0.0f ? f - 0.5f : f + 0.5f)); + int8.insert(int8.end(), PADDING, 0); } int Bias_correction::operator()(const Hsp &hsp) const @@ -119,7 +123,7 @@ int Bias_correction::operator()(const Hsp &hsp) const return (int)s; } -int Bias_correction::operator()(const Diagonal_segment &d) const +int Bias_correction::operator()(const DiagonalSegment &d) const { float s = 0; const int end = d.query_end(); @@ -159,7 +163,7 @@ vector hauser_global(const Composition& query_comp, const Composition& targ for (size_t i = 0; i < AMINO_ACID_COUNT; ++i) for (size_t j = 0; j < AMINO_ACID_COUNT; ++j) { double s = (double)score_matrix(i, j), q = i < TRUE_AA ? qscores[i] : 0.0, t = j < TRUE_AA ? tscores[j] : 0.0; - m[i * AMINO_ACID_COUNT + j] = std::round(s + std::min(q, t)); + m[i * AMINO_ACID_COUNT + j] = (int)std::round(s + std::min(q, t)); } return m; } diff --git a/src/stats/hauser_correction.h b/src/stats/hauser_correction.h index 2cce16420..47ff86646 100644 --- a/src/stats/hauser_correction.h +++ b/src/stats/hauser_correction.h @@ -23,7 +23,7 @@ along with this program. If not, see . #include #include #include "../basic/sequence.h" -#include "../basic/diagonal_segment.h" +#include "../util/geo/diagonal_segment.h" #include "../basic/match.h" struct No_score_correction @@ -41,7 +41,7 @@ struct Bias_correction : public std::vector score += (*this)[query_anchor + i * mult]; } int operator()(const Hsp &hsp) const; - int operator()(const Diagonal_segment &d) const; + int operator()(const DiagonalSegment &d) const; static std::vector reverse(const int8_t* p, const size_t len); std::vector int8; }; diff --git a/src/stats/matrix_adjust.cpp b/src/stats/matrix_adjust.cpp index fc6907016..05065a83a 100644 --- a/src/stats/matrix_adjust.cpp +++ b/src/stats/matrix_adjust.cpp @@ -113,6 +113,8 @@ along with this program. If not, see . #include "../lib/blast/nlm_linear_algebra.h" #include "cbs.h" +using std::vector; + namespace Stats { static constexpr int COMPO_NUM_TRUE_AA = 20; diff --git a/src/stats/matrix_adjust_eigen.cpp b/src/stats/matrix_adjust_eigen.cpp index dcb225910..0a1e943e9 100644 --- a/src/stats/matrix_adjust_eigen.cpp +++ b/src/stats/matrix_adjust_eigen.cpp @@ -412,7 +412,7 @@ static bool Blast_OptimizeTargetFrequencies(MatrixN& x, SolveReNewtonSystem(resids_x, resids_z, newton_system, workspace); /* Calculate a value of alpha that ensure that x is positive */ - const Float alpha = Nlm_StepBound(x, resids_x, Float(1.0 / .95)) * 0.95; + const Float alpha = Nlm_StepBound(x, resids_x, Float(1.0 / .95)) * Float(0.95); x += alpha * resids_x; z += alpha * resids_z; @@ -439,7 +439,7 @@ bool OptimizeTargetFrequencies(double* out, const double* joints_prob, const dou row_sums[i] = (Float)row_probs[i]; col_sums[i] = (Float)col_probs[i]; } - bool r = Blast_OptimizeTargetFrequencies(x, q, row_sums, col_sums, relative_entropy, tol, maxits); + bool r = Blast_OptimizeTargetFrequencies(x, q, row_sums, col_sums, (Float)relative_entropy, (Float)tol, maxits); for (size_t i = 0; i < N; ++i) for (size_t j = 0; j < N; ++j) out[i * N + j] = x(i, j); diff --git a/src/stats/score_matrix.cpp b/src/stats/score_matrix.cpp index 36787465d..2902635af 100644 --- a/src/stats/score_matrix.cpp +++ b/src/stats/score_matrix.cpp @@ -37,7 +37,17 @@ using std::vector; ScoreMatrix score_matrix; -ScoreMatrix::ScoreMatrix(const string & matrix, int gap_open, int gap_extend, int frameshift, int stop_match_score, uint64_t db_letters, int scale): +static Sls::AlignmentEvaluerParameters alp_params(const Stats::StandardMatrix* standard_matrix, int gap_open, int gap_extend, bool mmseqs_compat) { + if (mmseqs_compat) + return { 0.27359865037097330642, 0.044620920658722244834, 1.5938724404943873658, -19.959867650284412122, + 1.5938724404943873658, -19.959867650284412122, 30.455610143099914211, -622.28684628915891608, + 30.455610143099914211, -622.28684628915891608, 29.602444874818868215, -601.81087985041381216 }; + const Stats::StandardMatrix::Parameters& p = standard_matrix->constants(gap_open, gap_extend), & u = standard_matrix->ungapped_constants(); + const double G = gap_open + gap_extend, b = 2.0 * G * (u.alpha - p.alpha), beta = 2.0 * G * (u.alpha_v - p.alpha_v); + return Sls::AlignmentEvaluerParameters { p.Lambda, p.K, p.alpha, b, p.alpha, b, p.alpha_v, beta, p.alpha_v, beta, p.sigma, 2.0 * G * (u.alpha_v - p.sigma) }; +} + +ScoreMatrix::ScoreMatrix(const string & matrix, int gap_open, int gap_extend, int frameshift, int stop_match_score, uint64_t db_letters, int scale, bool mmseqs_compat): standard_matrix_(&Stats::StandardMatrix::get(matrix)), gap_open_ (gap_open == -1 ? standard_matrix_->default_gap_exist : gap_open), gap_extend_ (gap_extend == -1 ? standard_matrix_->default_gap_extend : gap_extend), @@ -57,10 +67,8 @@ ScoreMatrix::ScoreMatrix(const string & matrix, int gap_open, int gap_extend, in matrix8u_high_(standard_matrix_->scores.data(), stop_match_score, bias_, 16, 16), matrix16_(standard_matrix_->scores.data(), stop_match_score) { - const Stats::StandardMatrix::Parameters& p = standard_matrix_->constants(gap_open_, gap_extend_), & u = standard_matrix_->ungapped_constants(); - const double G = gap_open_ + gap_extend_, b = 2.0 * G * (u.alpha - p.alpha), beta = 2.0 * G * (u.alpha_v - p.alpha_v); - Sls::AlignmentEvaluerParameters params{ p.Lambda, p.K, p.alpha, b, p.alpha, b, p.alpha_v, beta, p.alpha_v, beta, p.sigma, 2.0 * G * (u.alpha_v - p.sigma) }; - evaluer.initParameters(params); + + evaluer.initParameters(alp_params(standard_matrix_, gap_open_, gap_extend_, mmseqs_compat)); ln_k_ = std::log(evaluer.parameters().K); init_background_scores(); } @@ -189,7 +197,7 @@ ScoreMatrix::Scores<_t>::Scores(const double (&freq_ratios)[Stats::NCBI_ALPH][St for (size_t i = 0; i < 32; ++i) for (size_t j = 0; j < 32; ++j) { if (i < TRUE_AA && j < TRUE_AA) - data[i * 32 + j] = std::round(std::log(freq_ratios[Stats::ALPH_TO_NCBI[i]][Stats::ALPH_TO_NCBI[j]]) / lambda * scale); + data[i * 32 + j] = (_t)std::round(std::log(freq_ratios[Stats::ALPH_TO_NCBI[i]][Stats::ALPH_TO_NCBI[j]]) / lambda * scale); else if (i < n && j < n) data[i * 32 + j] = (int)scores[i * n + j] * scale; else @@ -209,7 +217,13 @@ template struct ScoreMatrix::Scores; double ScoreMatrix::evalue(int raw_score, unsigned query_len, unsigned subject_len) const { - return evaluer.evalue((double)raw_score / scale_, query_len, subject_len) * (double)db_letters_ / (double)subject_len; + if (config.mmseqs_compat) { + const double epa = evaluer.evaluePerArea(raw_score); + const double a = evaluer.area(raw_score, query_len, db_letters_); + return epa * a; + } + else + return evaluer.evalue((double)raw_score / scale_, query_len, subject_len) * (double)db_letters_ / (double)subject_len; } double ScoreMatrix::evalue_norm(int raw_score, unsigned query_len, unsigned subject_len) const @@ -217,6 +231,13 @@ double ScoreMatrix::evalue_norm(int raw_score, unsigned query_len, unsigned subj return evaluer.evalue((double)raw_score / scale_, query_len, subject_len) * (double)1e9 / (double)subject_len; } +double ScoreMatrix::bitscore_corrected(int raw_score, unsigned query_len, unsigned subject_len) const +{ + //const double area = evaluer.area(raw_score, query_len, subject_len); + const double log_area = evaluer.log_area(raw_score, query_len, subject_len); + return (evaluer.parameters().lambda * raw_score - log(evaluer.parameters().K) - log_area) / log(2.0); +} + bool ScoreMatrix::report_cutoff(int score, double evalue) const { if (config.min_bit_score != 0) return bitscore(score) >= config.min_bit_score; @@ -232,3 +253,9 @@ void ScoreMatrix::init_background_scores() background_scores_[i] += Stats::blosum62.background_freqs[j] * (*this)(i, j); } } + +double ScoreMatrix::bitscore(double raw_score) const +{ + const double s = std::round(raw_score / scale_); // maintain compatibility with BLAST + return (lambda() * s - ln_k()) / LN_2; +} diff --git a/src/stats/score_matrix.h b/src/stats/score_matrix.h index 8d004738e..de4fbe776 100644 --- a/src/stats/score_matrix.h +++ b/src/stats/score_matrix.h @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2022 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -38,7 +38,7 @@ struct ScoreMatrix struct Custom {}; ScoreMatrix() :ln_k_(0.0) {} - ScoreMatrix(const std::string& matrix, int gap_open, int gap_extend, int frame_shift, int stop_match_score, uint64_t db_letters = 0, int scale = 1); + ScoreMatrix(const std::string& matrix, int gap_open, int gap_extend, int frame_shift, int stop_match_score, uint64_t db_letters = 0, int scale = 1, bool mmseqs_compat = false); ScoreMatrix(const std::string &matrix_file, int gap_open, int gap_extend, int stop_match_score, const Custom&, uint64_t db_letters = 0); friend std::ostream& operator<<(std::ostream& s, const ScoreMatrix&m); @@ -102,11 +102,7 @@ struct ScoreMatrix char bias() const { return bias_; } - double bitscore(double raw_score) const - { - const double s = std::round(raw_score / scale_); // maintain compatibility with BLAST - return ( lambda() * s - ln_k()) / LN_2; - } + double bitscore(double raw_score) const; double rawscore(double bitscore, double) const { return (bitscore*LN_2 + ln_k()) / lambda(); } @@ -116,6 +112,7 @@ struct ScoreMatrix double evalue(int raw_score, unsigned query_len, unsigned subject_len) const; double evalue_norm(int raw_score, unsigned query_len, unsigned subject_len) const; + double bitscore_corrected(int raw_score, unsigned query_len, unsigned subject_len) const; double evalue_norm(int raw_score, int query_len) const { @@ -197,6 +194,10 @@ struct ScoreMatrix return background_scores_; } + std::string name() const { + return name_; + } + double avg_id_score() const; bool report_cutoff(int score, double evalue) const; diff --git a/src/stats/stats.cpp b/src/stats/stats.cpp index 50076bc8d..ed3dff89f 100644 --- a/src/stats/stats.cpp +++ b/src/stats/stats.cpp @@ -24,6 +24,8 @@ along with this program. If not, see . #include "standard_matrix.h" using std::string; +using std::max; +using std::min; namespace Stats { @@ -51,4 +53,8 @@ const StandardMatrix::Parameters& StandardMatrix::ungapped_constants() const { return parameters.front(); } +double approx_id(Score raw_score, Loc range1, Loc range2) { + return min(max((double)raw_score / max(range1, range2) * 16.56 + 11.41, 0.0), 100.0); +} + } \ No newline at end of file diff --git a/src/stats/stats.h b/src/stats/stats.h new file mode 100644 index 000000000..f016b0576 --- /dev/null +++ b/src/stats/stats.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Stats { + +double approx_id(Score raw_score, Loc range1, Loc range2); + +} \ No newline at end of file diff --git a/src/test/simulate.cpp b/src/test/simulate.cpp deleted file mode 100644 index d88bbada4..000000000 --- a/src/test/simulate.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/**** -DIAMOND protein aligner -Copyright (C) 2013-2020 Max Planck Society for the Advancement of Science e.V. - Benjamin Buchfink - Eberhard Karls Universitaet Tuebingen - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -****/ - -#include -#include -#include "test.h" -#include "../util/util.h" -#include "../basic/config.h" -#include "../basic/sequence.h" -#include "../util/io/text_input_file.h" -#include "../util/seq_file_format.h" -#include "../util/sequence/sequence.h" -#include "../util/io/output_file.h" -#include "../stats/score_matrix.h" - -using std::vector; -using std::cout; -using std::endl; - -namespace Test { - -const double subst_freq[20][20] = { - { 0, 0.051599, 0.0319706, 0.0472072, 0.0259005, 0.042533, 0.0794012, 0.104447, 0.0190464, 0.0529913, 0.0806505, 0.0561607, 0.023709, 0.0243562, 0.0447981, 0.119439, 0.0710634, 0.00618423, 0.019181, 0.099362, }, - { 0.0836991,0,0.0557221,0.0526752,0.00570959,0.0834397,0.0992451,0.0474531,0.0360983,0.0290852,0.0591073,0.18726,0.0187483,0.0177066,0.03043,0.062954,0.0596622,0.00896391,0.0252332,0.036807, }, - { 0.0688047,0.0737048,0,0.134642,0.0104452,0.0556231,0.0811654,0.0970697,0.0503946,0.0214787,0.0370703,0.069479,0.0150052,0.0171298,0.0255345,0.114536,0.0729503,0.00573275,0.021037,0.0281966, }, - { 0.0734774,0.0538258,0.10774,0,0.00541203,0.0638292,0.220103,0.0852837,0.0313663,0.0145824,0.0242017,0.0687326,0.00866069,0.0125848,0.0451219,0.0885509,0.0562425,0.0041692,0.0139378,0.0221784, }, - { 0.186323,0.0324277,0.0277663,0.0177638,0,0.0153757,0.0222231,0.052226,0.0127764,0.0743205,0.11608,0.021819,0.0292451,0.0436977,0.0221956,0.0899075,0.0771679,0.00626418,0.0267835,0.125637, }, - { 0.0815328,0.112847,0.0568952,0.074416,0.00530242,0,0.150972,0.0456165,0.0364876,0.0241864,0.0523059,0.119369,0.0224538,0.0139128,0.0272128,0.0645326,0.0511373,0.00542559,0.0193477,0.0360457, }, - { 0.0918357,0.0766689,0.0556527,0.174685,0.00514321,0.105233,0,0.0497034,0.0284655,0.0215927,0.0364467,0.102734,0.0130324,0.0127301,0.0374955,0.0725947,0.0566717,0.00542622,0.0173958,0.0364932, }, - { 0.20364,0.0508512,0.0818626,0.0839228,0.0149571,0.0404233,0.0622734,0,0.0241239,0.0237986,0.038645,0.0567032,0.015483,0.0207614,0.0397716,0.122147,0.0536346,0.00808141,0.0181211,0.0407985, }, - { 0.0699983,0.0907505,0.0813351,0.0777822,0.0081228,0.0669371,0.0813018,0.0560113,0,0.0264461,0.0551068,0.0740711,0.0180528,0.03949,0.025928,0.0666684,0.0459882,0.0124538,0.0684967,0.0350589, }, - { 0.0758071,0.0218901,0.0124197,0.0102327,0.0139658,0.0158577,0.0178954,0.0154911,0.00998824,0,0.261344,0.0185933,0.0553554,0.0532969,0.0164271,0.0248502,0.0451354,0.00741016,0.0241487,0.299892, }, - { 0.0827941,0.0356979,0.0164472,0.0130886,0.0158172,0.0256513,0.0247873,0.0195319,0.0124665,0.218909,0,0.0302946,0.0895811,0.0919587,0.0199965,0.030285,0.046212,0.0136618,0.0364274,0.176391, }, - { 0.0781029,0.208329,0.0570106,0.0577245,0.00610149,0.0855974,0.10978,0.0459911,0.0280067,0.0234476,0.0526726,0,0.0186556,0.014372,0.0345505,0.0674287,0.0530917,0.00620819,0.0164983,0.0364304, }, - { 0.0820435,0.0371522,0.0193431,0.0143829,0.0129935,0.039314,0.0273425,0.02189,0.0123121,0.135883,0.247222,0.0313609,0,0.0639739,0.0174525,0.0360643,0.0483175,0.0103003,0.0273692,0.115283, }, - { 0.0624752,0.023874,0.0168958,0.0146007,0.0120188,0.0162837,0.0221759,0.0282569,0.025181,0.107736,0.228084,0.018739,0.0498004,0,0.0143663,0.0337512,0.0352976,0.040237,0.152406,0.09782, }, - { 0.137934,0.0566305,0.0383101,0.0798596,0.00858529,0.0448655,0.091169,0.0649277,0.0214561,0.0353504,0.0589727,0.0747337,0.0158675,0.0192857,0,0.0958676,0.0712531,0.00607841,0.0187647,0.0600885, }, - { 0.161678,0.0503686,0.0675213,0.0725064,0.0170918,0.0439576,0.0688274,0.0920208,0.023245,0.0261139,0.039281,0.0556799,0.0163333,0.0207061,0.0417712,0,0.140562,0.00529629,0.0168799,0.0401595, }, - { 0.116544,0.0543551,0.0502298,0.051343,0.0162852,0.0415458,0.0696321,0.0441794,0.0200499,0.0539524,0.072698,0.0528105,0.0237411,0.0226826,0.0364311,0.149828,0,0.00676344,0.0208007,0.0961279, }, - { 0.0616437,0.051267,0.0204352,0.0156793,0.0128913,0.0281531,0.0336645,0.0438026,0.0318158,0.0565597,0.135468,0.0329638,0.0367357,0.153548,0.018681,0.0425701,0.0372178,0,0.126995,0.0599093, }, - { 0.0629104,0.0481246,0.0279043,0.0248727,0.0132862,0.0319342,0.0336743,0.0255073,0.0636279,0.0693312,0.123129,0.03326,0.0336581,0.1923,0.0183125,0.0405417,0.039996,0.0424192,0,0.0752103, }, - { 0.122866,0.0265131,0.0140538,0.0140485,0.020662,0.0200589,0.0307184,0.022685,0.010223,0.253759,0.190633,0.0272914,0.0436694,0.0420276,0.0241076,0.034372,0.0705984,0.00696795,0.0247446,0, } }; - -vector generate_random_seq(size_t length, std::minstd_rand0 &random_engine) -{ - vector seq; - seq.reserve(length); - for (size_t i = 0; i < length; ++i) - seq.push_back(get_distribution<20>(score_matrix.background_freqs(), random_engine)); - return seq; -} - -void simulate_seqs() { - const size_t l = 300, n = atoi(config.seq_no[0].c_str()); - std::minstd_rand0 rand_engine; - for (size_t i = 0; i < n; ++i) { - cout << ">" << i << endl; - cout << Sequence(generate_random_seq(l, rand_engine)) << endl; - } -} - -vector simulate_homolog(const Sequence &seq, double id, std::minstd_rand0 &random_engine) -{ - vector out; - out.reserve(seq.length()); - std::uniform_int_distribution dist(0, 3); - for (Loc i = 0; i < seq.length(); ++i) - if ((double)rand() / RAND_MAX < id) - out.push_back(seq[i]); - else { - if (value_traits.alphabet_size == 5) - out.push_back(dist(random_engine)); - else - out.push_back(get_distribution<20>(subst_freq[(size_t)seq[i]], random_engine)); - } - return out; -} - -void mutate() { - TextInputFile in(config.single_query_file()); - string id; - vector seq; - input_value_traits = value_traits = nucleotide_traits; - OutputFile out(config.output_file); - std::minstd_rand0 random_engine; - std::uniform_real_distribution id_dist(0.3, 1.0); - while (FASTA_format().get_seq(id, seq, in, value_traits)) { - const double i = id_dist(random_engine); - Util::Seq::format(Sequence(simulate_homolog(Sequence(seq), i, random_engine)), std::to_string(int(i*100)).c_str(), nullptr, out, "fasta", nucleotide_traits); - } - out.close(); - in.close(); -} - -} \ No newline at end of file diff --git a/src/test/test.cpp b/src/test/test.cpp index 258b4860f..517dd2d13 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -27,7 +27,6 @@ along with this program. If not, see . #include #include #include "../util/io/temp_file.h" -#include "../util/io/text_input_file.h" #include "test.h" #include "../util/sequence/sequence.h" #include "../util/log_stream.h" @@ -39,6 +38,8 @@ along with this program. If not, see . #include "../util/system/system.h" #include "../data/dmnd/dmnd.h" #include "../basic/config.h" +#include "../data/fasta/fasta_file.h" +#include "../util/command_line_parser.h" using std::endl; using std::string; @@ -49,14 +50,16 @@ using std::shared_ptr; namespace Test { -size_t run_testcase(size_t i, shared_ptr &db, shared_ptr>& query_file, size_t max_width, bool bootstrap, bool log, bool to_cout) { +static size_t run_testcase(size_t i, shared_ptr &db, shared_ptr& query_file, size_t max_width, bool bootstrap, bool log, bool to_cout) { vector args = tokenize(test_cases[i].command_line, " "); args.emplace(args.begin(), "diamond"); if (log) args.push_back("--log"); - config = Config((int)args.size(), charp_array(args.begin(), args.end()).data(), false); + CommandLineParser parser; + config = Config((int)args.size(), charp_array(args.begin(), args.end()).data(), false, parser); statistics.reset(); - query_file->front().rewind(); + query_file->set_seqinfo_ptr(0); + db->set_seqinfo_ptr(0); if (to_cout) { Search::run(db, query_file); @@ -89,21 +92,22 @@ size_t run_testcase(size_t i, shared_ptr &db, shared_ptr> query_file(new list); - query_file->emplace_back(proteins); + FastaFile proteins("test1", true, FastaFile::WriteAccess()); + + shared_ptr query_file(new FastaFile("test2", true, FastaFile::WriteAccess())), db(new FastaFile("test3", true, FastaFile::WriteAccess())); + load_seqs(*query_file); + load_seqs(*db); timer.finish(); - config.command = Config::makedb; - TempFile *db_file; - DatabaseFile::make_db(&db_file, query_file.get()); - shared_ptr db(new DatabaseFile(*db_file)); - const size_t n = test_cases.size(), max_width = std::accumulate(test_cases.begin(), test_cases.end(), (size_t)0, [](size_t l, const TestCase& t) { return std::max(l, strlen(t.desc)); }); size_t passed = 0; @@ -112,9 +116,8 @@ int run() { cout << endl << "#Test cases passed: " << passed << '/' << n << endl; // << endl; - query_file->front().close_and_delete(); + query_file->close(); db->close(); - delete db_file; return passed == n ? 0 : 1; } diff --git a/src/test/test_cases.cpp b/src/test/test_cases.cpp index 177a2b209..4421357af 100644 --- a/src/test/test_cases.cpp +++ b/src/test/test_cases.cpp @@ -58,16 +58,16 @@ const vector ref_hashes = { 0x7ed13391c638dc2e, 0x98b810039a4e6e1b, 0x9a20976998759371, -0x91f5ca94f3965030, +0xa67de9d0530d5968, 0xa67de9d0530d5968, 0x3d593e440ca8eb97, 0x487a213a131d4958, 0x201a627d0d128fd5, 0xe787dcb23cc5b120, 0x5aa4baf48a888be9, -0xa2519e06e3bfa2fd, -0xe2781c840d096a80, -0x67b3a14cdd541dc3, +0x21f14583e88a13ac, +0x7f4b9df5947ae826, +0x713deb9a5ae4b9e }; } \ No newline at end of file diff --git a/src/tools/benchmark.cpp b/src/tools/benchmark.cpp index 124aa85ff..72129ca1b 100644 --- a/src/tools/benchmark.cpp +++ b/src/tools/benchmark.cpp @@ -40,6 +40,10 @@ along with this program. If not, see . #include "../stats/cbs.h" #include "../util/profiler.h" #include "../dp/swipe/cell_update.h" +#include "../dp/pfscan/pfscan.h" +#include "../dp/pfscan/simd.h" +#include "../dp/swipe/anchored.h" +#include "../dp/swipe/config.h" void benchmark_io(); @@ -179,12 +183,45 @@ void benchmark_transpose() { #endif #ifdef __SSE4_1__ + +void mt_swipe(const Sequence& s1, const Sequence& s2) { + //constexpr int CHANNELS = 16; + constexpr int CHANNELS = ::DISPATCH_ARCH::ScoreTraits>::CHANNELS; + static const size_t n = 100000llu; + DP::Targets targets; + for (size_t i = 0; i < CHANNELS; ++i) + targets[0].emplace_back(s2, s2.length(), 0, 0, Interval(), 0, 0, 0); + Bias_correction cbs(s1); + Statistics stat; + Sequence query = s1; + query.len_ = std::min(query.len_, (Loc)255); + auto dp_size = (n * query.length() * s2.length() * CHANNELS); + DP::Params params{ + query, "", Frame(0), query.length(), cbs.int8.data(), DP::Flags::FULL_MATRIX, HspValues(), stat, nullptr + }; + + auto f = [&]() { + for (size_t i = 0; i < n; ++i) { + //volatile list v = ::DP::BandedSwipe::ARCH_SSE4_1::swipe(targets, params); + volatile list v = ::DP::BandedSwipe::swipe(targets, params); + } + }; + using std::thread; + vector th; + high_resolution_clock::time_point t1 = high_resolution_clock::now(); + for (int i = 0; i < config.threads_; ++i) + th.emplace_back(f); + for (auto& t : th) + t.join(); + cout << "MT_SWIPE (int8_t):\t\t" << (double)duration_cast(high_resolution_clock::now() - t1).count() / dp_size * 1000 << " ps/Cell" << endl; +} + void swipe(const Sequence&s1, const Sequence&s2) { constexpr int CHANNELS = ::DISPATCH_ARCH::ScoreTraits>::CHANNELS; static const size_t n = 1000llu; DP::Targets targets; for (size_t i = 0; i < 32; ++i) - targets[0].emplace_back(s2, s2.length(), 0, 0, 0, 0); + targets[0].emplace_back(s2, s2.length(), 0, 0, Interval(), 0, 0, 0); Bias_correction cbs(s1); Statistics stat; Sequence query = s1; @@ -193,7 +230,7 @@ void swipe(const Sequence&s1, const Sequence&s2) { config.comp_based_stats = 4; Stats::TargetMatrix matrix(Stats::composition(s1), s1.length(), s2); DP::Params params{ - query, Frame(0), query.length(), cbs.int8.data(), DP::Flags::FULL_MATRIX, HspValues(), stat + query, "", Frame(0), query.length(), cbs.int8.data(), DP::Flags::FULL_MATRIX, HspValues(), stat, nullptr }; high_resolution_clock::time_point t1 = high_resolution_clock::now(); @@ -202,6 +239,14 @@ void swipe(const Sequence&s1, const Sequence&s2) { } cout << "SWIPE (int8_t):\t\t\t" << (double)duration_cast(high_resolution_clock::now() - t1).count() / dp_size * 1000 << " ps/Cell" << endl; + t1 = high_resolution_clock::now(); + targets[1] = targets[0]; + targets[0].clear(); + for (size_t i = 0; i < n; ++i) { + volatile list v = ::DP::BandedSwipe::swipe(targets, params); + } + cout << "SWIPE (int16_t):\t\t" << (double)duration_cast(high_resolution_clock::now() - t1).count() / dp_size * 1000 << " ps/Cell" << endl; + t1 = high_resolution_clock::now(); for (size_t i = 0; i < n; ++i) { volatile list v = ::DP::BandedSwipe::swipe(targets, params); @@ -234,13 +279,13 @@ void banded_swipe(const Sequence &s1, const Sequence &s2) { DP::Targets targets; //config.traceback_mode = TracebackMode::SCORE_BUFFER; for (size_t i = 0; i < 8; ++i) - targets[1].emplace_back(s2, s2.length(), -32, 32, 0, 0); + targets[1].emplace_back(s2, s2.length(), -32, 32, Interval(), 0, 0, 0); static const size_t n = 10000llu; //static const size_t n = 1llu; Statistics stat; Bias_correction cbs(s1); DP::Params params{ - s1, Frame(0), s1.length(), cbs.int8.data(), DP::Flags::NONE, HspValues(), stat + s1, "", Frame(0), s1.length(), cbs.int8.data(), DP::Flags::NONE, HspValues(), stat, nullptr }; high_resolution_clock::time_point t1 = high_resolution_clock::now(); for (size_t i = 0; i < n; ++i) { @@ -254,6 +299,7 @@ void banded_swipe(const Sequence &s1, const Sequence &s2) { } cout << "Banded SWIPE (int16_t):\t\t" << (double)duration_cast(high_resolution_clock::now() - t1).count() / (n * s1.length() * 65 * 16) * 1000 << " ps/Cell" << endl; + params.v = HspValues::TRANSCRIPT; t1 = high_resolution_clock::now(); for (size_t i = 0; i < n; ++i) { volatile auto out = ::DP::BandedSwipe::swipe(targets, params); @@ -261,12 +307,103 @@ void banded_swipe(const Sequence &s1, const Sequence &s2) { cout << "Banded SWIPE (int16_t, CBS, TB):" << (double)duration_cast(high_resolution_clock::now() - t1).count() / (n * s1.length() * 65 * 16) * 1000 << " ps/Cell" << endl; } +#if ARCH_ID == 2 + +void anchored_swipe(const Sequence& s1, const Sequence& s2) { + static const size_t n = 10000llu; + const auto s1_ = s1.subseq(0, 128); + const auto s2_ = s2.subseq(0, 128); + LongScoreProfile prof = DP::make_profile16(s1_, nullptr, 0); + LongScoreProfile prof8 = DP::make_profile8(s1_, nullptr, 0); + auto v = prof.pointers(0); + auto v8 = prof8.pointers(0); + vector> targets; + vector> profiles(32, prof8); + vector> pointers; + Statistics stats; + DP::AnchoredSwipe::Options options{ v.data(), v.data() }; + for (int i = 0; i < 32; ++i) { + pointers.push_back(profiles[i].pointers(0)); + //targets.push_back(DP::AnchoredSwipe::Target {s2_, -32, 32, { pointers.back().data(), nullptr}, s1_.length() }); + //targets.push_back(DP::AnchoredSwipe::Target(s2_, -32, 32, pointers[0].data(), s1_.length(), 0, false)); + targets.push_back(DP::AnchoredSwipe::Target(s2_, -32, 32, 0, s1_.length(), 0, false)); + } + const int cols = round_up(s2_.length(), DP::AnchoredSwipe::ARCH_AVX2::L); + + high_resolution_clock::time_point t1 = high_resolution_clock::now(); + for (size_t i = 0; i < n; ++i) { + DP::AnchoredSwipe::ARCH_AVX2::smith_waterman>(targets.data(), 32, options); + volatile auto x = targets[0].score; + } + cout << "Anchored Swipe (int8_t):\t" << (double)duration_cast(high_resolution_clock::now() - t1).count() / (n * cols * 64 * 32) * 1000 << " ps/Cell" << endl; + + vector> targets16; + for (int i = 0; i < 16; ++i) { + //targets16.push_back(DP::AnchoredSwipe::Target(s2_, -32, 32, v.data(), s1_.length(), 0, false)); + targets16.push_back(DP::AnchoredSwipe::Target(s2_, -32, 32, 0, s1_.length(), 0, false)); + } + t1 = high_resolution_clock::now(); + for (size_t i = 0; i < n; ++i) { + DP::AnchoredSwipe::ARCH_AVX2::smith_waterman>(targets16.data(), 16, options); + volatile auto x = targets[0].score; + } + cout << "Anchored Swipe (int16_t):\t" << (double)duration_cast(high_resolution_clock::now() - t1).count() / (n * cols * 64 * 16) * 1000 << " ps/Cell" << endl; + + DP::Targets dp_targets; + Anchor a(DiagonalSegment(0, 0, 0, 0), 0, 0, 0, 0, 0); + for (int i = 0; i < 16; ++i) + dp_targets[0].emplace_back(s2_, s2_.length(), -32, 32, Interval(), 0, 0, s1_.length(), nullptr, DpTarget::CarryOver(), a); + DP::AnchoredSwipe::Config cfg{ s1_, nullptr, 0, stats }; + + t1 = high_resolution_clock::now(); + for (size_t i = 0; i < n; ++i) { + DP::BandedSwipe::anchored_swipe(dp_targets, cfg); + volatile auto x = targets[0].score; + } + cout << "Anchored Swipe2 (int16_t):\t" << (double)duration_cast(high_resolution_clock::now() - t1).count() / (n * 128 * 64 * 16) * 1000 << " ps/Cell" << endl; +} + +//#endif +#endif + +void prefix_scan(const Sequence& s1, const Sequence& s2) { + static const size_t n = 100000llu; + const auto s1_ = s1.subseq(0, 150); + const auto s2_ = s2.subseq(0, 150); + Statistics stat; + Bias_correction cbs(s1_); + LongScoreProfile prof = DP::make_profile16(s1_, nullptr, 0); + LongScoreProfile prof8 = DP::make_profile8(s1_, nullptr, 0); + auto v = prof.pointers(0); + auto v8 = prof8.pointers(0); + DP::PrefixScan::Config cfg{ s1_,s2_, "", "", -32, 32, Interval(), v.data(), nullptr, v8.data(), nullptr, stat, 1000, 60, 0 }; + + high_resolution_clock::time_point t1 = high_resolution_clock::now(); + for (size_t i = 0; i < n; ++i) { + volatile auto out = DP::PrefixScan::align16(cfg); + } + cout << "Prefix Scan (int16_t):\t\t" << (double)duration_cast(high_resolution_clock::now() - t1).count() / (s2_.length() * 64 * n) * 1000 << " ps/Cell" << endl; + //statistics += stat; + //statistics.print(); + //statistics.reset(); + + cfg.score_bias = 0; + stat.reset(); + t1 = high_resolution_clock::now(); + for (size_t i = 0; i < n; ++i) { + volatile auto out = DP::PrefixScan::align8(cfg); + } + cout << "Prefix Scan (int8_t):\t\t" << (double)duration_cast(high_resolution_clock::now() - t1).count() / (s2_.length() * 64 * n) * 1000 << " ps/Cell" << endl; + statistics += stat; + //statistics.print(); +} + #ifdef __SSE4_1__ void diag_scores(const Sequence& s1, const Sequence& s2) { static const size_t n = 100000llu; high_resolution_clock::time_point t1 = high_resolution_clock::now(); Bias_correction cbs(s1); - LongScoreProfile p(s1, cbs); + LongScoreProfile p = DP::make_profile8(s1, cbs.int8.data(), 0); int scores[128]; for (size_t i = 0; i < n; ++i) { DP::scan_diags128(p, s2, -32, 0, (int)s2.length(), scores); @@ -343,9 +480,19 @@ void benchmark() { s3 = Sequence::from_string("ttfgrcavksnqagggtrshdwwpcqlrldvlrqfqpsqnplggdfdyaeafqsldyeavkkdiaalmtesqdwwpadfgnygglfvrmawhsagtyramdgrggggmgqqrfaplnswpdnqnldkarrliwpikqkygnkiswadlmlltgnvalenmgfktlgfgggradtwqsdeavywgaettfvpqgndvrynnsvdinaradklekplaathmgliyvnpegpngtpdpaasakdireafgrmgmndtetvaliagghafgkthgavkgsnigpapeaadlgmqglgwhnsvgdgngpnqmtsgleviwtktptkwsngyleslinnnwtlvespagahqweavngtvdypdpfdktkfrkatmltsdlalindpeylkisqrwlehpeeladafakawfkllhrdlgpttrylgpevp"); // d3ut2a1 s4 = Sequence::from_string("lvhvasvekgrsyedfqkvynaialklreddeydnyigygpvlvrlawhisgtwdkhdntggsyggtyrfkkefndpsnaglqngfkflepihkefpwissgdlfslggvtavqemqgpkipwrcgrvdtpedttpdngrlpdadkdagyvrtffqrlnmndrevvalmgahalgkthlknsgyegpggaannvftnefylnllnedwklekndanneqwdsksgymmlptdysliqdpkylsivkeyandqdkffkdfskafekllengitfpkdapspfifktleeqgl"); // d2euta_ - Sequence ss1 = Sequence(s1).subseq(34, s1.size()); - Sequence ss2 = Sequence(s2).subseq(33, s2.size()); + Sequence ss1 = Sequence(s1).subseq(34, (Loc)s1.size()); + Sequence ss2 = Sequence(s2).subseq(33, (Loc)s2.size()); +#ifdef __SSE4_1__ + //mt_swipe(s3, s4); +#endif +#if ARCH_ID == 2 +//#ifdef __SSE4_1__ + anchored_swipe(s1, s2); + //minimal_sw(s1, s2); +//#endif +#endif + prefix_scan(s1, s2); #ifdef __SSE4_1__ swipe(s3, s4); diag_scores(s1, s2); diff --git a/src/tools/benchmark_io.cpp b/src/tools/benchmark_io.cpp index 27eba1680..70a1fa644 100644 --- a/src/tools/benchmark_io.cpp +++ b/src/tools/benchmark_io.cpp @@ -20,6 +20,9 @@ along with this program. If not, see . #include #include +#ifdef WITH_BLASTDB +#include "../data/blastdb/blastdb.h" +#endif #include "../util/system/system.h" #include "../util/io/output_file.h" #include "../util/io/input_file.h" @@ -27,9 +30,6 @@ along with this program. If not, see . #include "../data/reference.h" #include "../util/io/input_stream_buffer.h" #include "../basic/config.h" -#ifdef WITH_BLASTDB -#include "../data/blastdb/blastdb.h" -#endif #include "../util/data_structures/deque.h" #define _REENTRANT #include "../lib/ips4o/ips4o.hpp" @@ -98,14 +98,14 @@ static void load_seqs() { config.chunk_size = 2.0; task_timer timer; timer.go("Opening the database"); - SequenceFile* db = SequenceFile::auto_create(config.database, SequenceFile::Flags::NONE); + SequenceFile* db = SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NONE); timer.finish(); message_stream << "Type: " << to_string(db->type()) << endl; Block* ref; while (true) { timer.go("Loading sequences"); - if ((ref = db->load_seqs((size_t)(config.chunk_size * 1e9), true, nullptr))->empty()) + if ((ref = db->load_seqs((size_t)(config.chunk_size * 1e9)))->empty()) return; size_t n = ref->seqs().letters() + ref->ids().letters(); message_stream << "Throughput: " << (double)n / (1 << 20) / timer.milliseconds() * 1000 << " MB/s" << endl; @@ -136,7 +136,7 @@ static void load_raw() { static void load_mmap() { static const size_t BUF = 2 * GIGABYTES; task_timer timer("Opening the database"); - SequenceFile* db = SequenceFile::auto_create(config.database, SequenceFile::Flags::NONE); + SequenceFile* db = SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NONE); timer.finish(); message_stream << "Type: " << to_string(db->type()) << endl; size_t n = db->sequence_count(), l = 0; @@ -156,13 +156,13 @@ static void load_mmap() { static void load_mmap_mt() { task_timer timer("Opening the database"); - SequenceFile* db = SequenceFile::auto_create(config.database, SequenceFile::Flags::NONE); + SequenceFile* db = SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NONE); timer.finish(); message_stream << "Type: " << to_string(db->type()) << endl; size_t n = db->sequence_count(); std::atomic_size_t i(0); vector threads; - for (size_t j = 0; j < config.threads_; ++j) + for (int j = 0; j < config.threads_; ++j) threads.emplace_back([&i, n, db] { size_t k, l = 0; vector v; @@ -181,11 +181,11 @@ static void load_mmap_mt() { void load_blast_seqid() { const size_t N = 100000; task_timer timer("Opening the database"); - SequenceFile* db = SequenceFile::auto_create(config.database, SequenceFile::Flags::NONE); + SequenceFile* db = SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NONE); timer.finish(); message_stream << "Type: " << to_string(db->type()) << endl; std::mt19937 g; - std::uniform_int_distribution dist(0, db->sequence_count() - 1); + std::uniform_int_distribution dist(0, (int)db->sequence_count() - 1); size_t n = 0; timer.go("Loading seqids"); for (size_t i = 0; i < N; ++i) { @@ -200,13 +200,13 @@ void load_blast_seqid() { void load_blast_seqid_lin() { task_timer timer("Opening the database"); - SequenceFile* db = SequenceFile::auto_create(config.database, SequenceFile::Flags::NONE); + SequenceFile* db = SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NONE); timer.finish(); message_stream << "Type: " << to_string(db->type()) << endl; size_t n = 0; - const size_t count = db->sequence_count(); + const int count = (int)db->sequence_count(); timer.go("Loading seqids"); - for (size_t i = 0; i < count; ++i) { + for (int i = 0; i < count; ++i) { auto l = ((BlastDB*)db)->db_->GetSeqIDs(i); n += l.size(); /*if (i % 1000 == 0) diff --git a/src/tools/find_shapes.cpp b/src/tools/find_shapes.cpp index d2e3bb13b..6f26d5751 100644 --- a/src/tools/find_shapes.cpp +++ b/src/tools/find_shapes.cpp @@ -13,12 +13,14 @@ using std::vector; using std::string; using std::array; +using Pattern = uint32_t; + static const int W = 7, L = 16, N = 64, T = 3; static array counts; -static set exclude; +static set exclude; -static void process_window(int p, set& patterns) { +static void process_window(int p, set& patterns) { int n = 0; array idx; for (int i = 1; i < L; ++i) @@ -36,7 +38,7 @@ static void process_window(int p, set& patterns) { } while (std::prev_permutation(bitmask.begin(), bitmask.end())); } -static void process_pattern(const string& s, set& patterns) { +static void process_pattern(const string& s, set& patterns) { int p = 0; for (size_t i = 0; i < s.length(); ++i) { p <<= 1; @@ -80,7 +82,7 @@ static bool is_id(const string& s) { } static void process_aln(vector::const_iterator begin, vector::const_iterator end) { - set patterns; + set patterns; for (auto i = begin; i < end; ++i) process_pattern(*i, patterns); @@ -91,10 +93,10 @@ static void process_aln(vector::const_iterator begin, vector::co ++counts[p]; } -static string as_string(int p) { +static string as_string(Pattern p) { string s; for (int i = 0; i < L; ++i) - if (p & (1 << i)) + if (p & (Pattern(1) << i)) s = '1' + s; else s = '0' + s; @@ -127,9 +129,9 @@ void find_shapes() { cout << "Processed = " << nt << endl; } in.close(); - size_t p_max = 0; + Pattern p_max = 0; size_t c_max = 0; - for (size_t p = 0; p < counts.size(); ++p) + for (Pattern p = 0; p < (Pattern)counts.size(); ++p) if (counts[p] > c_max) { p_max = p; c_max = counts[p]; diff --git a/src/tools/greedy_vertex_cover.cpp b/src/tools/greedy_vertex_cover.cpp new file mode 100644 index 000000000..eb8bfd5e7 --- /dev/null +++ b/src/tools/greedy_vertex_cover.cpp @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include +#include "../util/tsv/tsv.h" +#include "../util/tsv/file.h" +#include "../basic/config.h" +#include "../util/log_stream.h" +#include "../util/string/fixed_string.h" +#include "../util/string/tokenizer.h" +#include "../util/algo/algo.h" +#include "../util/system/system.h" + +using std::ofstream; +using std::mutex; +using std::lock_guard; +using std::endl; +using std::string; +using std::atomic; +using std::vector; +using std::unordered_map; +using std::cout; +using std::numeric_limits; +using std::runtime_error; +//using Acc = FixedString<32>; +using Acc = string; + +void greedy_vertex_cover() { + using Int = int64_t; + using Edge = Util::Algo::Edge; + const double cov = std::max(config.query_or_target_cover, config.member_cover); + const bool triplets = config.edge_format == "triplet"; + message_stream << "Coverage cutoff: " << cov << '%' << endl; + task_timer timer("Reading mapping file"); + //unordered_map acc2oid; + unordered_map acc2oid; + acc2oid.reserve(Util::Tsv::count_lines(config.database)); + TextInputFile mapping_file(config.database); + string query; + while (mapping_file.getline(), !mapping_file.line.empty() || !mapping_file.eof()) { + Util::String::Tokenizer(mapping_file.line, "\t") >> query; + acc2oid.emplace(query, acc2oid.size()); + } + mapping_file.close(); + timer.finish(); + message_stream << "#OIds: " << acc2oid.size() << endl; + if (acc2oid.size() > (size_t)numeric_limits::max()) + throw runtime_error("Input count exceeds supported maximum."); + + timer.go("Counting input lines"); + atomic lines(0); + std::function fn([&](int64_t, const char* begin, const char* end) { + Util::Tsv::LineIterator it(begin, end); + int64_t n = 0; + double qcov, tcov; + while (it.good()) { + string line = *it; + if (triplets) + ++n; + else { + Util::String::Tokenizer(line, "\t") >> Util::String::Skip() >> Util::String::Skip() >> qcov >> tcov; + if (qcov >= cov) + ++n; + if (tcov >= cov) + ++n; + } + ++it; + } + lines += n; + }); + Util::Tsv::File(Util::Tsv::Schema(), config.edges).read(config.threads_, fn); + timer.finish(); + message_stream << "#Lines: " << lines << endl; + + timer.go("Allocating memory"); + vector edges; + mutex mtx; + edges.reserve(lines); + + timer.go("Reading input lines"); + std::function fn2([&](int64_t, const char* begin, const char* end) { + Util::Tsv::LineIterator it(begin, end); + vector e; + string query, target; + double qcov, tcov, evalue; + while (it.good()) { + string line = *it; + Util::String::Tokenizer tok(line, "\t"); + tok >> query >> target; + if (!triplets) + tok >> qcov >> tcov; + tok >> evalue; + if (triplets || tcov >= cov || qcov >= cov) { + const auto q = acc2oid.at(query), t = acc2oid.at(target); + if (q == t) { + ++it; + continue; + } + if(triplets) + e.emplace_back(t, q, evalue); + else { + if (tcov >= cov) + e.emplace_back(q, t, evalue); + if (qcov >= cov) + e.emplace_back(t, q, evalue); + } + } + ++it; + } + { + lock_guard lock(mtx); + edges.insert(edges.end(), e.cbegin(), e.cend()); + } + }); + Util::Tsv::File(Util::Tsv::Schema(), config.edges).read(config.threads_, fn2); + timer.finish(); + log_rss(); + + timer.go("Making flat array"); + FlatArray edge_array = make_flat_array_dense(move(edges), (Int)acc2oid.size(), config.threads_, Edge::GetKey()); + timer.finish(); + log_rss(); + + auto r = Util::Algo::greedy_vertex_cover(edge_array, nullptr, !config.strict_gvc); + + timer.go("Building reverse mapping"); + vector acc(acc2oid.size()); + for (const auto& i : acc2oid) + //acc[i.second] = string(i.first.chars.data()); + acc[i.second] = i.first; + + timer.go("Generating output"); + int64_t c = 0; + ofstream centroid_out; + if (!config.centroid_out.empty()) + centroid_out = ofstream(config.centroid_out); + ofstream out; + if (!config.output_file.empty()) + out = ofstream(config.output_file); + for (int64_t i = 0; i < (int64_t)r.size(); ++i) { + if (r[i] == i) { + ++c; + if (!config.centroid_out.empty()) + centroid_out << acc[i] << endl; + } + if (!config.output_file.empty()) + out << acc[r[i]] << '\t' << acc[i] << endl; + } + centroid_out.close(); + out.close(); + timer.finish(); + message_stream << "#Centroids: " << c << endl; +} \ No newline at end of file diff --git a/src/tools/join.cpp b/src/tools/join.cpp index 2ffead17f..46907adc1 100644 --- a/src/tools/join.cpp +++ b/src/tools/join.cpp @@ -3,12 +3,13 @@ #include #include "../util/io/text_input_file.h" #include "../basic/config.h" -#include "../util/string/tsv.h" +#include "../util/tsv/tsv.h" #include "../util/string/tokenizer.h" #include "../util/log_stream.h" using std::cout; using std::endl; +using std::string; using namespace Util::Tsv; void join() { diff --git a/src/tools/merge_tsv.cpp b/src/tools/merge_tsv.cpp deleted file mode 100644 index e3e1c16e9..000000000 --- a/src/tools/merge_tsv.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "../basic/config.h" -#include "../util/io/text_input_file.h" -#include "../util/seq_file_format.h" -#include "../util/util.h" -#include "../util/log_stream.h" -#include "../util/string/tokenizer.h" - -using std::string; -using std::vector; -using std::endl; - -struct Record { - - enum { BLANK = -1 }; - - Record(): - query_id(BLANK) - { - } - - Record(TextInputFile &file, size_t file_id) : - query_id(BLANK), - file(file_id) - { - if (file.eof()) - return; - file.getline(); - if (file.line.empty()) - return; - Util::String::Tokenizer(file.line, "\t") >> query_id >> query_acc >> subject_acc >> evalue; - } - - bool operator<(const Record &r) const { - return query_id > r.query_id || (query_id == r.query_id && (evalue > r.evalue || (evalue == r.evalue && file > r.file))); - } - - bool blank() const { - return query_id == BLANK; - } - - friend std::ostream& operator<<(std::ostream& os, const Record& r) { - os << r.query_acc << '\t' << r.subject_acc << '\t' << r.evalue; - return os; - } - - int query_id; - string query_acc, subject_acc; - double evalue; - size_t file; - -}; - -void merge_tsv() { - if (config.input_ref_file.empty()) - throw std::runtime_error("Missing parameter --in"); - task_timer timer("Processing input"); - - const size_t n = config.input_ref_file.size(); - message_stream << "#Input files: " << n << endl; - vector files; - files.reserve(n); - std::priority_queue queue; - size_t records = 0; - for (size_t i = 0; i < n; ++i) { - files.emplace_back(config.input_ref_file[i]); - queue.emplace(files.back(), i); - ++records; - } - - while (!queue.empty()) { - if (queue.top().blank()) { - queue.pop(); - continue; - } - std::cout << queue.top() << endl; - const size_t file = queue.top().file; - - Record r; - do - r = Record(files[file], file); - while (!r.blank() && queue.top().query_acc == r.query_acc && queue.top().subject_acc == r.subject_acc); - queue.pop(); - if (!r.blank()) { - queue.push(std::move(r)); - ++records; - } - } - - for (auto &f : files) - f.close(); - message_stream << "#Records: " << records << endl; -} \ No newline at end of file diff --git a/src/tools/roc.cpp b/src/tools/roc.cpp index 8f82bd2ec..682cea603 100644 --- a/src/tools/roc.cpp +++ b/src/tools/roc.cpp @@ -112,7 +112,7 @@ struct Histogram { static constexpr double MAX_EV = 10000.0; Histogram() : - bin_offset(-std::floor((double)DBL_MIN_EXP* log(2.0)* config.log_evalue_scale)), + bin_offset(int(-std::floor((double)DBL_MIN_EXP* log(2.0)* config.log_evalue_scale))), bin_count(bin_offset + int(std::round(std::log(MAX_EV) * config.log_evalue_scale)) + 1), false_positives(bin_count, 0), coverage(bin_count, 0.0) @@ -374,7 +374,7 @@ void roc() { fam_count[i->second] = config.family_cap; vector threads; - for (unsigned i = 0; i < std::min(config.threads_, 6u); ++i) + for (int i = 0; i < std::min(config.threads_, 6); ++i) threads.emplace_back(worker); timer.go("Processing alignments"); diff --git a/src/tools/rocid.cpp b/src/tools/rocid.cpp index afc229f4b..931e73098 100644 --- a/src/tools/rocid.cpp +++ b/src/tools/rocid.cpp @@ -11,6 +11,8 @@ using std::endl; using std::array; using std::unordered_multimap; using std::map; +using std::vector; +using std::string; struct Assoc { uint8_t id, fam_idx; diff --git a/src/tools/tools.cpp b/src/tools/tools.cpp index e19ed53bb..042ea0e32 100644 --- a/src/tools/tools.cpp +++ b/src/tools/tools.cpp @@ -1,6 +1,8 @@ +#include #include #include #include +#include #include "tsv_record.h" #include "../basic/config.h" #include "../basic/value.h" @@ -18,6 +20,12 @@ #include "../lib/ips4o/ips4o.hpp" #include "../masking/masking.h" #include "../util/ptr_vector.h" +#include "../util/string/tokenizer.h" +#include "../util/algo/algo.h" +#include "../util/data_structures/hash_table.h" +#include "../util/string/fixed_string.h" +#include "../util/tsv/table.h" +#include "../util/tsv/file.h" using std::array; using std::cout; @@ -25,6 +33,14 @@ using std::endl; using std::vector; using std::unique_ptr; using std::pair; +using std::unordered_map; +using std::cerr; +using std::ofstream; +using std::string; +using Util::Tsv::Schema; +using Util::Tsv::Type; +using Util::Tsv::TokenIterator; +using Util::Seq::FastaIterator; void filter_blasttab() { TextInputFile in(""); @@ -38,8 +54,8 @@ void filter_blasttab() { } else ++query_hit; - if(query_hit < config.max_alignments && r.evalue <= config.max_evalue) - cout << r << endl; + /*if (query_hit < config.max_alignments && r.evalue <= config.max_evalue) + cout << r << endl;*/ } } @@ -47,18 +63,21 @@ void split() { TextInputFile in(config.single_query_file()); string id; vector seq; - size_t n = 0, f = 0, b = (size_t)(config.chunk_size * 1e9); - OutputFile *out = new OutputFile(std::to_string(f) + ".faa.gz", Compressor::ZLIB); + size_t n = 0, f = 0, b = (size_t)(config.chunk_size * 1e9), seqs = 0; + OutputFile *out = new OutputFile(std::to_string(f) + ".faa.zst", Compressor::ZSTD); while (FASTA_format().get_seq(id, seq, in, value_traits)) { if (n >= b) { out->close(); delete out; - out = new OutputFile(std::to_string(++f) + ".faa.gz", Compressor::ZLIB); + out = new OutputFile(std::to_string(++f) + ".faa.zst", Compressor::ZSTD); n = 0; } string blast_id = Util::Seq::seqid(id.c_str(), false); Util::Seq::format(Sequence(seq), blast_id.c_str(), nullptr, *out, "fasta", amino_acid_traits); n += seq.size(); + ++seqs; + if (seqs % 1000000 == 0) + std::cerr << "#Sequences processed: " << seqs << " #letters:" << n << endl; } out->close(); delete out; @@ -84,7 +103,7 @@ void hash_seqs() { while (fmt.get_seq(id, seq, f, amino_acid_traits)) { array hash; hash.fill('\0'); - MurmurHash3_x64_128(seq.data(), seq.size(), hash.data(), hash.data()); + MurmurHash3_x64_128(seq.data(), (int)seq.size(), hash.data(), hash.data()); cout << Util::Seq::seqid(id.c_str(), false) << '\t' << hex_print(hash.data(), 16) << endl; } f.close(); @@ -107,17 +126,17 @@ void list_seeds() { void finish() {} vector& seeds; }; - unique_ptr db(SequenceFile::auto_create(config.database)); - unique_ptr block(db->load_seqs(SIZE_MAX)); + unique_ptr db(SequenceFile::auto_create({ config.database })); + unique_ptr block(db->load_seqs(INT64_MAX)); mask_seqs(block->seqs(), Masking::get(), true, MaskingAlgo::TANTAN); vector seeds; seeds.reserve(block->seqs().letters()); PtrVector cb; cb.push_back(new Callback{ seeds }); auto parts = block->seqs().partition(1); - ::shapes = ShapeConfig(config.shape_mask.empty() ? Search::shape_codes.at(Sensitivity::DEFAULT) : config.shape_mask, config.shapes); + ::shapes = ShapeConfig(config.shape_mask.empty() ? Search::shape_codes[(int)align_mode.sequence_type].at(Sensitivity::DEFAULT) : config.shape_mask, config.shapes); Reduction::reduction = Reduction("A R N D C Q E G H I L K M F P S T W Y V"); - EnumCfg cfg{ &parts, 0, 1, SeedEncoding::SPACED_FACTOR, nullptr, false, false, config.seed_cut_, MaskingAlgo::NONE }; + EnumCfg cfg{ &parts, 0, 1, SeedEncoding::SPACED_FACTOR, nullptr, false, false, config.seed_cut_, MaskingAlgo::NONE, 0 }; enum_seeds(*block, cb, &no_filter, cfg); ips4o::parallel::sort(seeds.begin(), seeds.end()); @@ -135,4 +154,46 @@ void list_seeds() { s = Reduction::reduction.decode_seed(i->second, shapes[0].weight_); cout << i->first << '\t' << s << endl; } -} \ No newline at end of file +} + +using Acc = FixedString<32>; + +void index_fasta() { + HashTable table(size_t(config.max_target_seqs_ * 1.2), Acc::Hash()); + std::ifstream f(config.query_file.front()); + string l; + int64_t n = 0; + for (;;) { + int64_t p = f.tellg(); + if (!std::getline(f, l)) + break; + if (l.empty() || l[0] != '>') + continue; + auto e = table.insert(Acc(Util::Seq::seqid(l.c_str() + 1, false))); + e->value = p + 1; + ++n; + } + OutputFile out(config.query_file.front() + ".htidx"); + out.write(table.data(), table.size()); + out.close(); + message_stream << "#Sequences: " << n << endl; +} + +void fetch_seq() { + +} + +void length_sort() { + Schema schema{ Type::STRING, Type::STRING }; + Util::Tsv::Table table(schema); +} + +#ifdef EXTRA + +void sort() { + Util::Tsv::File input(Schema{ Type::INT64, Type::STRING }, config.query_file.front()); + //auto f = input.sort(0, 1, config.output_file); + //delete f; +} + +#endif \ No newline at end of file diff --git a/src/tools/view.cpp b/src/tools/view.cpp index 5c33b8a6c..6d2343ccf 100644 --- a/src/tools/view.cpp +++ b/src/tools/view.cpp @@ -4,12 +4,13 @@ #include "../basic/config.h" #include "../data/sequence_file.h" #include "../util/string/tokenizer.h" -#include "../util/string/tsv.h" +#include "../util/tsv/tsv.h" #include "../dp/dp.h" #include "../output/output_format.h" #include "../output/output.h" #include "../masking/masking.h" #include "../util/sequence/sequence.h" +#include "../util/string/fixed_string.h" using std::unique_ptr; using std::endl; @@ -19,25 +20,8 @@ using std::thread; using std::list; using std::array; using std::thread; - -template -struct FixedString { - FixedString(const string& s) { - if (s.length() >= L) - throw std::runtime_error("FixedString"); - std::copy(s.begin(), s.end(), chars.begin()); - chars[s.length()] = '\0'; - } - bool operator==(const FixedString& s) const { - return strcmp(chars.data(), s.chars.data()) == 0; - } - array chars; - struct Hash { - size_t operator()(const FixedString& s) const { - return std::hash()(string(s.chars.data())); - } - }; -}; +using std::string; +using std::vector; using Acc = FixedString<30>; @@ -71,7 +55,7 @@ static SequenceSet get_seqs(const vector& accs) { } static TextBuffer* view_query(const string& query_acc, const string& buf, SequenceFile& query_file, SequenceFile& target_file, Search::Config& cfg, Statistics& stats) { - static const size_t BATCH_SIZE = 1024; + static const BlockId BATCH_SIZE = 1024; const vector target_acc = Util::Tsv::extract_column(buf, 1); //SequenceSet targets = target_file.seqs_by_accession(target_acc.begin(), target_acc.end()); //vector query = query_file.seq_by_accession(query_acc); @@ -86,27 +70,27 @@ static TextBuffer* view_query(const string& query_acc, const string& buf, Sequen if (cfg.query_masking != MaskingAlgo::NONE) Masking::get()(query.data(), query.size(), cfg.query_masking, 0); if (cfg.target_masking != MaskingAlgo::NONE) - for (size_t i = 0; i < targets.size(); ++i) + for (BlockId i = 0; i < targets.size(); ++i) Masking::get()(targets.ptr(i), targets.length(i), cfg.target_masking, 0); const auto query_comp = Stats::composition(Sequence(query)); const int query_len = Stats::count_true_aa(Sequence(query)); list hsp; - for (size_t t = 0; t < target_acc.size(); t += BATCH_SIZE) { - const size_t t1 = std::min(t + BATCH_SIZE, target_acc.size()); + for (BlockId t = 0; t < (BlockId)target_acc.size(); t += BATCH_SIZE) { + const BlockId t1 = std::min(t + BATCH_SIZE, (BlockId)target_acc.size()); vector matrices; - for (size_t i = t; i < t1; ++i) + for (BlockId i = t; i < t1; ++i) matrices.emplace_back(query_comp, query_len, targets[i]); const HspValues v = HspValues::COORDS | HspValues::IDENT | HspValues::LENGTH; DP::Targets dp_targets; - for (size_t i = t; i < t1; ++i) + for (BlockId i = t; i < t1; ++i) if (targets.length(i) > 0) - dp_targets[DP::BandedSwipe::bin(v, query.size(), 0, 0, 0, 0, 0)].emplace_back(targets[i], targets[i].length(), i, &matrices[i - t]); + dp_targets[DP::BandedSwipe::bin(v, (Loc)query.size(), 0, 0, 0, 0, 0)].emplace_back(targets[i], targets[i].length(), i, &matrices[i - t]); - DP::Params params{ Sequence(query), Frame(0), (Loc)query.size(), nullptr, DP::Flags::FULL_MATRIX, v, stats }; + DP::Params params{ Sequence(query), "", Frame(0), (Loc)query.size(), nullptr, DP::Flags::FULL_MATRIX, v, stats, nullptr }; hsp.splice(hsp.end(), DP::BandedSwipe::swipe(dp_targets, params)); } @@ -114,9 +98,11 @@ static TextBuffer* view_query(const string& query_acc, const string& buf, Sequen Blast_tab_format fmt; TranslatedSequence query_seq; TextBuffer* out = new TextBuffer(); + Output::Info info{ SeqInfo { 0, 0, query_acc.c_str(), "", query_seq.source().length(), query_seq.source(), Sequence()}, false, nullptr, *out, {} }; for (Hsp& h : hsp) { h.query_source_range = h.query_range; fmt.print_match(HspContext(h, + 0, 0, query_seq, query_acc.c_str(), @@ -125,7 +111,7 @@ static TextBuffer* view_query(const string& query_acc, const string& buf, Sequen target_acc[h.swipe_target].c_str(), 0, 0, - Sequence()), cfg, *out); + Sequence()), info); } return out; @@ -142,13 +128,13 @@ void view_tsv() { throw std::runtime_error("Too many arguments for query file (--query/-q)"); task_timer timer("Opening the database file"); - unique_ptr db(SequenceFile::auto_create(config.database, SequenceFile::Flags::NO_FASTA)); + unique_ptr db(SequenceFile::auto_create({ config.database }, SequenceFile::Flags::NO_FASTA)); score_matrix = ScoreMatrix("blosum62", -1, -1, 1, 0); score_matrix.set_db_letters(config.db_size ? config.db_size : db->letters()); Masking::instance = unique_ptr(new Masking(score_matrix)); timer.go("Opening the query file"); - unique_ptr query_file(SequenceFile::auto_create(config.query_file.front(), SequenceFile::Flags::NO_FASTA)); + unique_ptr query_file(SequenceFile::auto_create(config.query_file, SequenceFile::Flags::NO_FASTA)); /*if (db->type() != SequenceFile::Type::BLAST) // || query_file->type() != SequenceFile::Type::BLAST) throw std::runtime_error("BLAST database required.");*/ @@ -158,13 +144,14 @@ void view_tsv() { timer.go("Opening the output file"); OutputFile output_file(config.output_file); - OutputSink::instance = unique_ptr(new OutputSink(0, &output_file)); + OutputWriter writer{ &output_file }; + output_sink.reset(new ReorderQueue(0, writer)); timer.go("Loading database"); - db_block = db->load_seqs(SIZE_MAX, true, nullptr, true, false); + db_block = db->load_seqs(SIZE_MAX, nullptr, SequenceFile::LoadFlags::ALL); timer.go("Loading queries"); - query_block = query_file->load_seqs(SIZE_MAX, true, nullptr, true, false); + query_block = query_file->load_seqs(SIZE_MAX, nullptr, SequenceFile::LoadFlags::ALL); timer.go("Building accession mapping"); unsigned n = db_block->ids().size(); @@ -206,7 +193,7 @@ void view_tsv() { delete out; } else - OutputSink::instance->push(q, out); + output_sink->push(q, out); } } catch (std::exception& e) { @@ -215,13 +202,14 @@ void view_tsv() { }; vector threads; - for (size_t i = 0; i < config.threads_; ++i) + for (int i = 0; i < config.threads_; ++i) threads.emplace_back(worker); for (auto& t : threads) t.join(); timer.go("Closing the output file"); output_file.close(); + output_sink.reset(); delete db_block; delete query_block; } \ No newline at end of file diff --git a/src/util/algo/algo.h b/src/util/algo/algo.h index d7bc2479d..762463d17 100644 --- a/src/util/algo/algo.h +++ b/src/util/algo/algo.h @@ -3,17 +3,37 @@ #include #include #include "partition.h" +#define _REENTRANT +#include "../../lib/ips4o/ips4o.hpp" +#include "../data_structures/flat_array.h" +#include "../../basic/value.h" namespace Util { namespace Algo { +template struct Edge { - uint32_t v1, v2, weight; - bool operator<(const Edge &x) const { - return weight > x.weight; + Edge() + {} + using Key = Int; + Edge(Key node1, Key node2, double weight) : + node1(node1), + node2(node2), + weight(weight) + {} + bool operator<(const Edge& e) const { + return node1 < e.node1 || (node1 == e.node1 && node2 < e.node2); } + struct GetKey { + Key operator()(const Edge& e) const { + return e.node1; + } + }; + Key node1, node2; + double weight; }; -std::vector greedy_vertex_cover(std::vector> &neighbors); +template +std::vector greedy_vertex_cover(FlatArray>& neighbors, const SuperBlockId* member_counts = nullptr, bool merge_recursive = false); template size_t merge_capped(It i0, const It i1, It j0, const It j1, const size_t cap, Out out) { @@ -63,4 +83,15 @@ std::vector partition_table(It begin, It end, size_t n, Key key) { return v; } +template +std::vector> sort_by_value(const It begin, const It end, const int threads) { + std::vector> out; + out.reserve(end - begin); + for (auto i = begin; i != end; ++i) + out.emplace_back(*i, i - begin); + ips4o::parallel::sort(out.begin(), out.end(), std::less>(), threads); + return out; +} + + }} \ No newline at end of file diff --git a/src/util/algo/greedy_vertex_cover.cpp b/src/util/algo/greedy_vertex_cover.cpp index 649d22fcd..eebab1e87 100644 --- a/src/util/algo/greedy_vertex_cover.cpp +++ b/src/util/algo/greedy_vertex_cover.cpp @@ -1,67 +1,133 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + #include -#include -#include +#include +#include #include "algo.h" +#include "../log_stream.h" -using namespace std; +using std::vector; +using std::priority_queue; +using std::pair; +using std::numeric_limits; +using std::swap; namespace Util { namespace Algo { -struct GreedyVertexCover { - - GreedyVertexCover(vector> &neighbors) : - reverse_neighbors(neighbors.size()) { - const int n = (int)neighbors.size(); - centroid.insert(centroid.begin(), n, -1); - for (int i = 0; i < n; ++i) { - for (int j : neighbors[i]) - reverse_neighbors[j].push_back(i); +template +static Int neighbor_count2(It begin, It end, const vector& centroids) { + Int n = 0, last = -1; + for (It i = begin; i != end; ++i) + if (centroids[i->node2] == -1 && i->node2 != last) { + ++n; + last = i->node2; } + return n; +} - vector merged; - for (int i = 0; i < n; ++i) { - std::sort(neighbors[i].begin(), neighbors[i].end()); - std::sort(reverse_neighbors[i].begin(), reverse_neighbors[i].end()); - merged.clear(); - std::set_union(neighbors[i].begin(), neighbors[i].end(), reverse_neighbors[i].begin(), reverse_neighbors[i].end(), std::back_inserter(merged)); - neighbors[i] = merged; +template +static Int neighbor_count(It begin, It end, const vector& centroids) { + Int n = 0, last = -1; + It w = begin; + for (It i = begin; i != end; ++i) { + if (i->node1 == -1) + break; + if (centroids[i->node2] == -1 && i->node2 != last) { + ++n; + last = i->node2; + if (w < i) + swap(w, i); + ++w; } + } + if (w < end) + w->node1 = -1; + return n; +} - its.reserve(n); - for (int i = 0; i < n; ++i) { - its.push_back(count_to_idx.insert(make_pair((int)neighbors[i].size(), i))); - } +template +static Int neighbor_count(Int node, It begin, It end, const vector& centroids, const SuperBlockId* member_counts) { + Int n = member_counts[node]; + for (It i = begin; i != end; ++i) + if (centroids[i->node2] == -1) + n += member_counts[i->node2]; + return n; +} - while (!count_to_idx.empty()) { - const int i = count_to_idx.rbegin()->second; - assign_centroid(i, i, neighbors); - for (int j : neighbors[i]) - if (centroid[j] == -1) - assign_centroid(j, i, neighbors); - } +template +static void fix_assignment(vector& centroids) { + for (Int i = 0; i < (Int)centroids.size();) { + if (centroids[centroids[i]] != centroids[i]) + centroids[i] = centroids[centroids[i]]; + else + ++i; } +} + +template +vector greedy_vertex_cover(FlatArray>& neighbors, const SuperBlockId* member_counts, bool merge_recursive) { + task_timer timer("Computing edge counts"); + priority_queue> q; + vector centroids(neighbors.size(), -1); + for (Int i = 0; i < neighbors.size(); ++i) + q.emplace(member_counts ? neighbor_count(i, neighbors.cbegin(i), neighbors.cend(i), centroids, member_counts) : + (Int)neighbors.count(i), i); - void assign_centroid(int i, int c, const vector>& neighbors) { - centroid[i] = c; - count_to_idx.erase(its[i]); - for (int j : neighbors[i]) { - if (centroid[j] >= 0) - continue; - auto it = count_to_idx.insert(make_pair(its[j]->first - 1, j)); - count_to_idx.erase(its[j]); - its[j] = it; + timer.go("Computing vertex cover"); + while (!q.empty()) { + const Int node = q.top().second; + q.pop(); + if (centroids[node] != -1) + continue; + const Int count = member_counts ? neighbor_count(node, neighbors.cbegin(node), neighbors.cend(node), centroids, member_counts) : + neighbor_count(neighbors.begin(node), neighbors.end(node), centroids); + if (!q.empty() && count < q.top().first) + q.emplace(count, node); + else { + centroids[node] = node; + for (auto i = neighbors.cbegin(node); i != neighbors.cend(node); ++i) + if (centroids[i->node2] == -1 || (merge_recursive && centroids[i->node2] == i->node2)) + centroids[i->node2] = node; } } - vector centroid; - multimap count_to_idx; - vector::iterator> its; - vector> reverse_neighbors; + timer.go("Computing reassignment"); + vector weights(neighbors.size(), numeric_limits::lowest()); + for (Int node = 0; node < neighbors.size(); ++node) + if (centroids[node] == node) + for (auto i = neighbors.cbegin(node); i != neighbors.cend(node); ++i) + if (centroids[i->node2] != i->node2 && i->weight > weights[i->node2]) { + weights[i->node2] = i->weight; + centroids[i->node2] = node; + } -}; + if (merge_recursive) { + timer.go("Computing merges"); + fix_assignment(centroids); + } -vector greedy_vertex_cover(vector> &neighbors) { - return GreedyVertexCover(neighbors).centroid; + return centroids; } +template vector greedy_vertex_cover(FlatArray>& neighbors, const SuperBlockId* member_counts, bool merge_recursive); +template vector greedy_vertex_cover(FlatArray>& neighbors, const SuperBlockId* member_counts, bool merge_recursive); + }} \ No newline at end of file diff --git a/src/util/algo/hash_join.h b/src/util/algo/hash_join.h index a5fcaab12..3284b3e96 100644 --- a/src/util/algo/hash_join.h +++ b/src/util/algo/hash_join.h @@ -53,24 +53,25 @@ void hash_table_join( DoubleArray &dst_r, DoubleArray &dst_s) { - typedef HashTable> Table; + using Key = typename _t::Key; + typedef HashTable, NoModulo> Table; - uint32_t N = (uint32_t)next_power_of_2(R.n * config.join_ht_factor); - Table table(N, ExtractBits(N, shift)); + Key N = (Key)next_power_of_2(R.n * config.join_ht_factor); + Table table(N, ExtractBits(N, shift)); typename Table::Entry *p; for (_t *i = R.data; i < R.end(); ++i) { p = table.insert(i->key); - ++p->r; - i->key = unsigned(p - table.data()); + ++p->value.r; + i->key = Key(p - table.data()); } _t *hit_s = S.data; for (_t *i = S.data; i < S.end(); ++i) { if ((p = table.find_entry(i->key))) { - ++p->s; + ++p->value.s; hit_s->value = i->value; - hit_s->key = unsigned(p - table.data()); + hit_s->key = Key(p - table.data()); ++hit_s; } } @@ -79,12 +80,12 @@ void hash_table_join( for (unsigned i = 0; i < table.size(); ++i) { p = &table.data()[i]; - if (p->s) { - unsigned r = p->r, s = p->s; + if (p->value.s) { + unsigned r = p->value.r, s = p->value.s; it_r.count() = r; it_s.count() = s; - p->r = dst_r.offset(it_r) + 4; - p->s = dst_s.offset(it_s) + 4; + p->value.r = dst_r.offset(it_r) + 4; + p->value.s = dst_s.offset(it_s) + 4; it_r.next(); it_s.next(); } @@ -94,16 +95,16 @@ void hash_table_join( for (const _t *i = R.data; i < R.end(); ++i) { p = &table.data()[i->key]; - if (p->s) { - dst_r[p->r] = i->value; - p->r += sizeof(typename _t::Value); + if (p->value.s) { + dst_r[p->value.r] = i->value; + p->value.r += sizeof(typename _t::Value); } } for (const _t *i = S.data; i < hit_s; ++i) { p = &table.data()[i->key]; - dst_s[p->s] = i->value; - p->s += sizeof(typename _t::Value); + dst_s[p->value.s] = i->value; + p->value.s += sizeof(typename _t::Value); } } @@ -116,8 +117,9 @@ void table_join( DoubleArray &dst_r, DoubleArray &dst_s) { - const unsigned keys = 1 << (total_bits - shift); - ExtractBits key(keys, shift); + using Key = typename _t::Key; + const Key keys = (Key)1 << (total_bits - shift); + ExtractBits key(keys, shift); RelPtr *table = (RelPtr*)calloc(keys, sizeof(RelPtr)); RelPtr *p; @@ -134,7 +136,7 @@ void table_join( typename DoubleArray::Iterator it_r = dst_r.begin(), it_s = dst_s.begin(); - for (unsigned i = 0; i < keys; ++i) { + for (Key i = 0; i < keys; ++i) { p = &table[i]; if (p->s) { unsigned r = p->r, s = p->s; diff --git a/src/util/algo/join_result.h b/src/util/algo/join_result.h index aa17b6260..30cf12787 100644 --- a/src/util/algo/join_result.h +++ b/src/util/algo/join_result.h @@ -16,9 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef JOIN_RESULT_H_ -#define JOIN_RESULT_H_ - +#pragma once #include #include "../range.h" #include "../data_structures/double_array.h" @@ -134,4 +132,3 @@ struct JoinResult : public vector*, DoubleA }; */ -#endif \ No newline at end of file diff --git a/src/util/algo/merge_files.h b/src/util/algo/merge_files.h new file mode 100644 index 000000000..4061580a6 --- /dev/null +++ b/src/util/algo/merge_files.h @@ -0,0 +1,47 @@ +#include +#include "../io/serialize.h" +#include "../io/exceptions.h" + +template +void merge_sorted_files(const It begin, const It end, F& f) { + struct Entry { + Entry(T&& value, ptrdiff_t idx): + value(value), + idx(idx) + {} + Entry(const Entry& e): + value(e.value), + idx(e.idx) + {} + Entry& operator=(const Entry& e) { + value = e.value; + idx = e.idx; + return *this; + } + bool operator<(const Entry& e) const { + return !(value < e.value); + } + T value; + ptrdiff_t idx; + }; + std::priority_queue q; + std::vector> d; + for (It i = begin; i != end; ++i) { + d.emplace_back(*i); + try { + q.emplace(d.back().get(), i - begin); + } + catch (EndOfStream&) { + } + } + while (!q.empty()) { + f(q.top().value); + const ptrdiff_t idx = q.top().idx; + q.pop(); + try { + q.emplace(d[idx].get(), idx); + } + catch (EndOfStream&) { + } + } +} \ No newline at end of file diff --git a/src/util/algo/radix_sort.h b/src/util/algo/radix_sort.h index 233db12dc..ba815b33b 100644 --- a/src/util/algo/radix_sort.h +++ b/src/util/algo/radix_sort.h @@ -38,7 +38,7 @@ void radix_sort(_t* begin, _t* end, uint32_t max_key, size_t threads) { if(threads > 1) parallel_radix_cluster<_t, _get_key>(Relation<_t>(in, n), i * config.radix_bits, out, threads); else { - unsigned *hst = new unsigned[1 << config.radix_bits]; + unsigned *hst = new unsigned[(size_t)1 << config.radix_bits]; radix_cluster< _t, _get_key>(Relation<_t>(in, n), i * config.radix_bits, out, hst); delete[] hst; } diff --git a/src/util/algo/sort_helper.h b/src/util/algo/sort_helper.h index 52c5ecb94..f965933d6 100644 --- a/src/util/algo/sort_helper.h +++ b/src/util/algo/sort_helper.h @@ -21,15 +21,15 @@ along with this program. If not, see . #pragma once #include -template +template struct SortedListJoiner { - SortedListJoiner(It1& it1, It2& it2, Cmp cmp, Value value) : + SortedListJoiner(It1& it1, It2& it2, Value value) : it1_(&it1), it2_(&it2), - cmp_(cmp), value_(value) { + next(); } bool good() { @@ -47,38 +47,41 @@ struct SortedListJoiner { if (!it1_->good()) return; - if (!cmp_(v2, **it1_) && !cmp_(**it1_, v2)) + if (Key2()(v2) == Key1()(**it1_)) return; ++(*it2_); if (!it2_->good()) return; - if (!cmp_(v1, **it2_) && !cmp_(**it2_, v1)) - throw std::runtime_error("Duplicate keys: " + v1.first); + if (Key1()(v1) == Key2()(**it2_)) + throw std::runtime_error("Duplicate keys: " + Key1()(v1)); + next(); + } + +private: + + void next() { do { - if (cmp_(**it1_, **it2_)) + if (Key1()(**it1_) < Key2()(**it2_)) ++(*it1_); - else if (cmp_(**it2_, **it1_)) + else if (Key2()(**it2_) < Key1()(**it1_)) ++(*it2_); else return; } while (good()); } -private: - It1* it1_; It2* it2_; - const Cmp cmp_; const Value value_; }; -template -SortedListJoiner join_sorted_lists(It1& it1, It2& it2, Cmp cmp, Value value) { - return SortedListJoiner(it1, it2, cmp, value); +template +SortedListJoiner join_sorted_lists(It1& it1, It2& it2, Key1, Key2, Value value) { + return SortedListJoiner(it1, it2, value); } template diff --git a/src/util/algo/transform_iterator.h b/src/util/algo/transform_iterator.h new file mode 100644 index 000000000..b8924923e --- /dev/null +++ b/src/util/algo/transform_iterator.h @@ -0,0 +1,44 @@ +#pragma once +#include + +template +struct TransformIterator { + using difference_type = typename It::difference_type; + using iterator_category = std::random_access_iterator_tag; + using reference = typename std::result_of::type; + using value_type = reference; + using pointer = reference*; + TransformIterator(It it, const F& f) : + it_(it), + f_(f) + {} + reference operator*() const { + return f_(*it_); + } + TransformIterator& operator++() { + ++it_; + return *this; + } + TransformIterator& operator--() { + --it_; + return *this; + } + TransformIterator& operator+=(difference_type d) { + it_ += d; + return *this; + } + bool operator!=(const TransformIterator& it) const { + return it_ != it.it_; + } + difference_type operator-(const TransformIterator& it) const { + return it_ - it.it_; + } +private: + It it_; + F f_; +}; + +template +TransformIterator transform(It it, const F& f) { + return TransformIterator(it, f); +} \ No newline at end of file diff --git a/src/util/algo/upgma.cpp b/src/util/algo/upgma.cpp deleted file mode 100644 index dbc14c7e8..000000000 --- a/src/util/algo/upgma.cpp +++ /dev/null @@ -1,200 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../../lib/MemoryPool/MemoryPool.h" -#include "../io/text_input_file.h" -#include "../../basic/config.h" -#include "../string/tokenizer.h" -#include "../log_stream.h" - -using std::string; -using std::list; -using std::endl; -using std::map; -using std::vector; -using std::ostream; - -namespace Util { namespace Algo { namespace UPGMA { - -struct Edge { - Edge(int n1, int n2, double d): - n1(n1), - n2(n2), - deleted(0), - d(d) - {} - bool operator<(const Edge &e) const { - return d < e.d; - } - int target(int me) const { - if (n1 == me) - return n2; - else if (n2 == me) - return n1; - else - throw std::runtime_error("Edge::target"); - } - friend ostream& operator<<(ostream &s, const Edge &e) { - s << e.n1 << '\t' << e.n2 << '\t' << e.d; - return s; - } - int n1, n2, deleted; - double d; -}; - -typedef list> EdgeList; -typedef EdgeList::iterator EdgePtr; - -struct CmpEdge { - bool operator()(const EdgePtr& e, const EdgePtr &f) const { - return e->d > f->d; - } -}; - -typedef std::priority_queue, CmpEdge> Queue; - -struct Node { - Node(int size, int parent): - size(size), - parent(parent) - {} - void sort_neighbors() { - std::sort(neighbors.begin(), neighbors.end(), CmpNeighbor{ parent }); - } - void set_parent(int parent, EdgeList &edges) { - this->parent = parent; - for (EdgePtr e : neighbors) { - ++e->deleted; - if (e->deleted == 3) - edges.erase(e); - } - neighbors.clear(); - neighbors.shrink_to_fit(); - } - struct CmpNeighbor { - int me; - bool operator()(const EdgePtr &e, const EdgePtr &f) const { - return e->target(me) < f->target(me); - } - }; - int size; - int parent; - vector neighbors; -}; - -void merge_nodes(int n1, - int n2, - vector &nodes, - EdgeList &edges, - Queue &queue, - double max_dist) { - const int union_idx = (int)nodes.size(); - nodes.emplace_back(nodes[n1].size + nodes[n2].size, union_idx); - Node &node1 = nodes[n1], &node2 = nodes[n2], &union_node = nodes.back(); - - vector::iterator i = node1.neighbors.begin(), j = node2.neighbors.begin(); - while (i < node1.neighbors.end() || j < node2.neighbors.end()) { - int it = i < node1.neighbors.end() ? (*i)->target(n1) : INT_MAX, jt = j < node2.neighbors.end() ? (*j)->target(n2) : INT_MAX; - double d1, d2; - if (it == jt) { - d1 = (*i)->d; - d2 = (*j)->d; - ++i; - ++j; - } - else if (it < jt) { - d1 = (*i)->d; - d2 = max_dist; - ++i; - } - else { - d1 = max_dist; - d2 = (*j)->d; - it = jt; - ++j; - } - if (nodes[it].parent != it || it == n1 || it == n2) - continue; - const EdgePtr e = edges.emplace(edges.end(), it, union_idx, (node1.size * d1 + node2.size * d2) / (node1.size + node2.size)); - queue.push(e); - union_node.neighbors.push_back(e); - nodes[it].neighbors.push_back(e); - } - node1.set_parent(union_idx, edges); - node2.set_parent(union_idx, edges); -} - -void upgma(EdgeList &edges, size_t node_count) { - const double max_dist = 10.0; - vector edge_vec; - vector nodes; - - message_stream << "Building node vector..." << endl; - nodes.reserve(node_count); - for (int i = 0; in1].neighbors.push_back(i); - nodes[i->n2].neighbors.push_back(i); - } - - message_stream << "Sorting neighborhoods..." << endl; - for (Node &node : nodes) - node.sort_neighbors(); - - message_stream << "Building priority queue..." << endl; - Queue queue(CmpEdge(), std::move(edge_vec)); - - message_stream << "Clustering nodes..." << endl; - while (!queue.empty()) { - EdgePtr e = queue.top(); - queue.pop(); - if (nodes[e->n1].parent == e->n1 && nodes[e->n2].parent == e->n2 && e->d < max_dist) { - merge_nodes(e->n1, e->n2, nodes, edges, queue, max_dist); - --node_count; - } - ++e->deleted; - if (e->deleted == 3) - edges.erase(e); - if (edges.size() % 10000 == 0) - message_stream << "#Edges: " << edges.size() << ", #Nodes: " << node_count << endl; - } -} - -void upgma() { - TextInputFile in(config.single_query_file()); - string query, target; - double evalue; - EdgeList edges; - map acc2idx; - - message_stream << "Reading edges..." << endl; - while (in.getline(), !in.eof()) { - String::Tokenizer t(in.line, "\t"); - t >> query >> target >> evalue; - if (acc2idx.find(query) == acc2idx.end()) - acc2idx[query] = acc2idx.size(); - if (acc2idx.find(target) == acc2idx.end()) - acc2idx[target] = acc2idx.size(); - const int query_idx = acc2idx[query], target_idx = acc2idx[target]; - if (query_idx < target_idx) - edges.emplace_back(query_idx, target_idx, evalue); - if (edges.size() % 10000 == 0 && !edges.empty()) - message_stream << "#Edges read: " << edges.size() << endl; - } - message_stream << "#Edges: " << edges.size() << ", #Nodes: " << acc2idx.size() << endl; - in.close(); - upgma(edges, acc2idx.size()); -} - -}}} \ No newline at end of file diff --git a/src/util/algo/upgma_mc.cpp b/src/util/algo/upgma_mc.cpp deleted file mode 100644 index ec9af516e..000000000 --- a/src/util/algo/upgma_mc.cpp +++ /dev/null @@ -1,294 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../io/text_input_file.h" -#include "../../basic/config.h" -#include "../string/tokenizer.h" -#include "../log_stream.h" -#include "../../lib/MemoryPool/MemoryPool.h" -#include "edge_vec.h" - -using std::string; -using std::list; -using std::endl; -using std::map; -using std::vector; -using std::ostream; -using std::pair; -using std::cout; - -namespace Util { namespace Algo { namespace UPGMA_MC { - -struct Edge { - Edge(int n1, int n2, int count, double s): - n1(n1), - n2(n2), - deleted(0), - count(count), - s(s), - l(s), - u(s) - {} - void set_bounds(double lambda, double max_dist, double max_edges) { - l = (s + lambda * (max_edges - count)) / max_edges; - u = (s + max_dist * (max_edges - count)) / max_edges; - } - bool operator<=(const Edge &e) const { - return u <= e.l; - } - int target(int me) const { - if (n1 == me) - return n2; - else if (n2 == me) - return n1; - else - throw std::runtime_error("Edge::target"); - } - friend ostream& operator<<(ostream &s, const Edge &e) { - s << e.n1 << '\t' << e.n2 << '\t' << e.l << '\t' << e.u; - return s; - } - int n1, n2, deleted, count; - double s, l, u; -}; - -typedef list> EdgeList; -typedef EdgeList::iterator EdgePtr; - -struct CmpEdge { - bool operator()(const EdgePtr& e, const EdgePtr &f) const { - //return e->l > f->l || (e->l == f->l && (e->n1 > f->n1 || (e->n1 == f->n1 && e->n2 > f->n2))); - return e->l > f->l || (e->l == f->l && e->u > f->u); - } -}; - -typedef std::priority_queue, CmpEdge> Queue; - -void erase(EdgePtr &e, EdgeList &edges) { - ++e->deleted; - if (e->deleted == 3) - edges.erase(e); -} - -struct Node { - Node(int idx, int size, int parent): - idx(idx), - size(size), - parent(parent) - {} - void sort_neighbors() { - std::sort(neighbors.begin(), neighbors.end(), CmpNeighbor{ parent }); - } - void set_parent(int parent, EdgeList &edges) { - this->parent = parent; - for (EdgePtr e : neighbors) - erase(e, edges); - neighbors.clear(); - neighbors.shrink_to_fit(); - } - bool root() const { - return parent == idx; - } - struct CmpNeighbor { - int me; - bool operator()(const EdgePtr &e, const EdgePtr &f) const { - return e->target(me) < f->target(me); - } - }; - int idx, size, parent; - vector neighbors; -}; - -bool valid(const EdgePtr &e, const vector &nodes) { - return nodes[e->n1].root() && nodes[e->n2].root(); -} - -void merge_nodes(int n1, - int n2, - vector &nodes, - EdgeList &edges, - Queue &queue, - double max_dist, - double lambda) { - const int union_idx = (int)nodes.size(); - nodes.emplace_back(union_idx, nodes[n1].size + nodes[n2].size, union_idx); - Node &node1 = nodes[n1], &node2 = nodes[n2], &union_node = nodes.back(); - - vector::iterator i = node1.neighbors.begin(), j = node2.neighbors.begin(); - while (i < node1.neighbors.end() || j < node2.neighbors.end()) { - int it = i < node1.neighbors.end() ? (*i)->target(n1) : INT_MAX, jt = j < node2.neighbors.end() ? (*j)->target(n2) : INT_MAX; - double s; - int edge_count; - if (it == jt) { - s = (*i)->s + (*j)->s; - edge_count = (*i)->count + (*j)->count; - ++i; - ++j; - } - else if (it < jt) { - s = (*i)->s; - edge_count = (*i)->count; - ++i; - } - else { - s = (*j)->s; - edge_count = (*j)->count; - it = jt; - ++j; - } - if (nodes[it].parent != it || it == n1 || it == n2) - continue; - const double max_edges = (double)union_node.size*(double)nodes[it].size; - const EdgePtr e = edges.emplace(edges.end(), it, union_idx, edge_count, s); - e->set_bounds(lambda, max_dist, max_edges); - queue.push(e); - union_node.neighbors.push_back(e); - nodes[it].neighbors.push_back(e); - } - node1.set_parent(union_idx, edges); - node2.set_parent(union_idx, edges); -} - -int parent(int idx, const vector &nodes) { - while (nodes[idx].parent != idx) - idx = nodes[idx].parent; - return idx; -} - -double load_edges(EdgeVec& all_edges, EdgeList &edges, vector &nodes, Queue &queue, double lambda, double max_dist) { - message_stream << "Clearing neighborhoods..." << endl; - for (Node &node : nodes) { - node.neighbors.clear(); - node.neighbors.shrink_to_fit(); - } - - message_stream << "Clearing old edges..." << endl; - for (EdgeList::iterator i = edges.begin(); i != edges.end();) - if (!valid(i, nodes)) - i = edges.erase(i); - else { - i->deleted = 0; - ++i; - } - - if (edges.size() >= config.upgma_edge_limit) - throw std::runtime_error("Edge limit"); - - message_stream << "Building edge hash map..." << endl; - std::unordered_map edge_map; - edge_map.reserve(edges.size()); - for (EdgePtr i = edges.begin(); i != edges.end(); ++i) - edge_map[(uint64_t(i->n1) << 32) | i->n2] = i; - - message_stream << "Setting parents..." << endl; - for (Node &node : nodes) - while (nodes[node.parent].parent != node.parent) - node.parent = nodes[node.parent].parent; - - double evalue = lambda; - message_stream << "Reading edges..." << endl; - CompactEdge edge; - while (edges.size() < config.upgma_edge_limit && (edge = all_edges.get())) { - const int query_idx = edge.n1, target_idx = edge.n2; - evalue = edge.d; - //cout << edge.n1 << '\t' << edge.n2 << '\t' << evalue << endl; - - //int i = parent(query_idx, nodes), j = parent(target_idx, nodes); - int i = nodes[query_idx].parent, j = nodes[target_idx].parent; - if (i == query_idx && j == target_idx) { - if (i >= j) std::swap(i, j); - edges.emplace_back(i, j, 1, evalue); - } - else { - if (i >= j) std::swap(i, j); - const auto e = edge_map.find((uint64_t(i) << 32) | j); - if (e == edge_map.end()) { - const EdgePtr f = edges.emplace(edges.end(), i, j, 1, evalue); - edge_map[(uint64_t(i) << 32) | j] = f; - } - else { - ++e->second->count; - e->second->s += evalue; - } - } - /*if (edges.size() % 10000 == 0 && !edges.empty()) - message_stream << "#Edges: " << edges.size() << endl;*/ - } - - lambda = edge ? evalue : max_dist; - - message_stream << "Recomputing bounds, building edge vector and neighborhood..." << endl; - vector edge_vec; - edge_vec.reserve(edges.size()); - for (EdgeList::iterator i = edges.begin(); i != edges.end(); ++i) { - i->set_bounds(lambda, max_dist, (double)nodes[i->n1].size * (double)nodes[i->n2].size); - edge_vec.push_back(i); - nodes[i->n1].neighbors.push_back(i); - nodes[i->n2].neighbors.push_back(i); - } - - message_stream << "Sorting neighborhoods..." << endl; - for (Node &node : nodes) - node.sort_neighbors(); - - message_stream << "Building priority queue..." << endl; - queue = Queue(CmpEdge(), std::move(edge_vec)); - message_stream << "#Edges: " << edges.size() << endl; - - return lambda; -} - -void upgma() { - const double max_dist = (dist_type() == DistType::BITSCORE) ? 0.0 : 10.0; - message_stream << "Reading edges..." << endl; - EdgeVec all_edges(config.single_query_file().c_str()); - message_stream << "Read " << all_edges.nodes() << " nodes, " << all_edges.size() << " edges." << endl; - - EdgeList edges; - vector nodes; - for (int i = 0; i < (int)all_edges.nodes(); ++i) - nodes.emplace_back(i, 1, i); - Queue queue; - double lambda = (dist_type() == DistType::BITSCORE) ? -100.0 : 0.0; - int node_count = (int)nodes.size(), round = 0; - do { - lambda = load_edges(all_edges, edges, nodes, queue, lambda, max_dist); - message_stream << "Clustering nodes..." << endl; - message_stream << "#Edges: " << edges.size() << ", #Nodes: " << node_count << endl; - while (!queue.empty()) { - EdgePtr e = queue.top(); - queue.pop(); - while (!queue.empty() && !valid(queue.top(), nodes)) { - EdgePtr f = queue.top(); - erase(f, edges); - queue.pop(); - } - if (!queue.empty() && !(*e <= *queue.top())) { - //std::cerr << e->u << '\t' << queue.top()->l << '\t' << all_edges.print(e->n1) << '\t' << all_edges.print(e->n2) << '\t' << lambda << endl; - queue.push(e); - break; - } - if (valid(e, nodes) && e->u < max_dist) { - merge_nodes(e->n1, e->n2, nodes, edges, queue, max_dist, lambda); - --node_count; - cout << nodes.back().parent << '\t' << all_edges.print(e->n1) << '\t' << all_edges.print(e->n2) << '\t' << e->u << endl; - } - erase(e, edges); - if (edges.size() % 10000 == 0) - message_stream << "#Edges: " << edges.size() << ", #Nodes: " << node_count << endl; - } - message_stream << "#Edges: " << edges.size() << ", #Nodes: " << node_count << endl; - ++round; - } while (lambda < max_dist); -} - -}}} \ No newline at end of file diff --git a/src/util/async_buffer.h b/src/util/async_buffer.h index 0435bfd8b..73c284a5b 100644 --- a/src/util/async_buffer.h +++ b/src/util/async_buffer.h @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -43,8 +43,10 @@ struct AsyncBuffer { typedef std::vector Vector; + using Key = typename SerializerTraits::Key; + static const int64_t ENTRY_SIZE = (int64_t)sizeof(T); - AsyncBuffer(size_t input_count, const std::string &tmpdir, unsigned bins, const SerializerTraits& traits) : + AsyncBuffer(Key input_count, const std::string &tmpdir, int bins, const SerializerTraits& traits) : bins_(bins), bin_size_((input_count + bins_ - 1) / bins_), input_count_(input_count), @@ -54,7 +56,7 @@ struct AsyncBuffer { log_stream << "Async_buffer() " << input_count << ',' << bin_size_ << std::endl; count_ = new std::atomic_size_t[bins]; - for (unsigned i = 0; i < bins; ++i) { + for (int i = 0; i < bins; ++i) { tmp_file_.push_back(new AsyncFile()); count_[i] = (size_t)0; } @@ -64,14 +66,14 @@ struct AsyncBuffer delete[] count_; } - size_t begin(size_t bin) const + Key begin(int bin) const { - return bin*bin_size_; + return (Key)bin*bin_size_; } - size_t end(size_t bin) const + Key end(int bin) const { - return std::min((bin + 1)*bin_size_, input_count_); + return std::min(Key(bin + 1)*bin_size_, input_count_); } struct Iterator : public Writer @@ -82,14 +84,14 @@ struct AsyncBuffer parent_(parent) { ser_.reserve(parent.bins_); - for (unsigned i = 0; i < parent.bins_; ++i) { + for (int i = 0; i < parent.bins_; ++i) { ser_.emplace_back(buffer_[i], parent.traits_); out_.push_back(&parent.tmp_file_[i]); } } virtual Iterator& operator=(const T& x) override { - const unsigned bin = ser_.front().traits.key(x) / parent_.bin_size_; + const int bin = int(ser_.front().traits.key(x) / parent_.bin_size_); if (SerializerTraits::is_sentry(x)) { if (buffer_[bin].size() >= buffer_size) flush(bin); @@ -100,14 +102,14 @@ struct AsyncBuffer ser_[bin] << x; return *this; } - void flush(unsigned bin) + void flush(int bin) { out_[bin]->write(buffer_[bin].data(), buffer_[bin].size()); buffer_[bin].clear(); } virtual ~Iterator() { - for (unsigned bin = 0; bin < parent_.bins_; ++bin) { + for (int bin = 0; bin < parent_.bins_; ++bin) { flush(bin); parent_.count_[bin] += count_[bin]; } @@ -121,8 +123,9 @@ struct AsyncBuffer AsyncBuffer &parent_; }; - void load(size_t max_size) { - auto worker = [this](size_t end) { + void load(int64_t max_size) { + max_size = std::max(max_size, (int64_t)1); + auto worker = [this](int end) { for (; bins_processed_ < end; ++bins_processed_) load_bin(*data_next_, bins_processed_); }; @@ -130,8 +133,9 @@ struct AsyncBuffer data_next_ = nullptr; return; } - size_t size = count_[bins_processed_], end = bins_processed_ + 1, current_size, disk_size = tmp_file_[bins_processed_].tell(); - while (end < bins_ && (size + (current_size = count_[end])) * sizeof(T) < max_size) { + int64_t size = count_[bins_processed_], current_size, disk_size = tmp_file_[bins_processed_].tell(); + int end = bins_processed_ + 1; + while (end < bins_ && (size + (current_size = count_[end])) * ENTRY_SIZE < max_size) { size += current_size; disk_size += tmp_file_[end].tell(); ++end; @@ -145,15 +149,15 @@ struct AsyncBuffer load_worker_ = new std::thread(worker, end); } - std::tuple*, size_t, size_t> retrieve() { + std::tuple*, Key, Key> retrieve() { if (data_next_ != nullptr) { load_worker_->join(); delete load_worker_; } - return std::tuple*, size_t, size_t> { data_next_, input_range_next_.first, input_range_next_.second }; + return std::tuple*, Key, Key> { data_next_, input_range_next_.first, input_range_next_.second }; } - unsigned bins() const + int bins() const { return bins_; } @@ -162,6 +166,10 @@ struct AsyncBuffer return total_disk_size_; } + int64_t bin_size(int i) const { + return count_[i]; + } + private: void load_bin(std::vector &out, size_t bin) @@ -177,13 +185,14 @@ struct AsyncBuffer f.close_and_delete(); } - const unsigned bins_; - const size_t bin_size_, input_count_; + const int bins_; + const Key bin_size_, input_count_; const SerializerTraits traits_; - size_t bins_processed_, total_disk_size_; + int bins_processed_; + int64_t total_disk_size_; PtrVector tmp_file_; std::atomic_size_t *count_; - std::pair input_range_next_; + std::pair input_range_next_; std::vector* data_next_; std::thread* load_worker_; diff --git a/src/util/binary_buffer.h b/src/util/binary_buffer.h index ab1894310..1c9374090 100644 --- a/src/util/binary_buffer.h +++ b/src/util/binary_buffer.h @@ -16,9 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef BINARY_BUFFER_H_ -#define BINARY_BUFFER_H_ - +#pragma once #include #include #include @@ -26,15 +24,12 @@ along with this program. If not, see . #include "intrin.h" #include "algo/varint.h" -using std::vector; -using std::string; - -struct BinaryBuffer : public vector +struct BinaryBuffer : public std::vector { struct Iterator { - Iterator(vector::const_iterator begin, vector::const_iterator end): + Iterator(std::vector::const_iterator begin, std::vector::const_iterator end): ptr_ (begin), end_ (end) { } @@ -42,17 +37,17 @@ struct BinaryBuffer : public vector { read(x); return *this; } Iterator& operator>>(uint8_t &x) { read(x); return *this; } - template - void read(_t &x) + template + void read(T &x) { - check(sizeof(_t)); - x = *(_t*)(&*ptr_); - ptr_ += sizeof(_t); + check(sizeof(T)); + x = *(T*)(&*ptr_); + ptr_ += sizeof(T); } - template - void read(vector<_t> &v, size_t count) + template + void read(std::vector &v, size_t count) { - const size_t l = sizeof(_t) * count; + const size_t l = sizeof(T) * count; check(l); v.resize(count); memcpy(v.data(), &*ptr_, l); @@ -78,7 +73,7 @@ struct BinaryBuffer : public vector { ::read_varint(*this, dst); } - Iterator& operator>>(string &dst) + Iterator& operator>>(std::string &dst) { dst.clear(); char c; @@ -91,12 +86,10 @@ struct BinaryBuffer : public vector private: void check(size_t size) const { if(ptr_+size > end_) throw std::runtime_error("Unexpected end of file."); } - vector::const_iterator ptr_, end_; + std::vector::const_iterator ptr_, end_; }; Iterator begin() const - { return Iterator (vector::begin(), vector::end()); } + { return Iterator (std::vector::begin(), std::vector::end()); } }; - -#endif /* BINARY_BUFFER_H_ */ diff --git a/src/util/command_line_parser.cpp b/src/util/command_line_parser.cpp index 964073982..a81cd7d45 100644 --- a/src/util/command_line_parser.cpp +++ b/src/util/command_line_parser.cpp @@ -26,28 +26,23 @@ along with this program. If not, see . using namespace std; -Command_line_parser & Command_line_parser::add(const Options_group & group) -{ - for (PtrVector::const_iterator i = group.options.begin(); i != group.options.end(); ++i) { - map_[(*i)->id] = *i; - map_short_[(*i)->short_id] = *i; - } - groups_.push_back(&group); - return *this; +OptionsGroup& CommandLineParser::add_group(const char *title, const std::vector& commands, bool disabled) { + groups_.emplace_back(title, commands, disabled, this); + return groups_.back(); } -Command_line_parser & Command_line_parser::add_command(const char * s, const char *desc, unsigned code) +CommandLineParser & CommandLineParser::add_command(const char * s, const char *desc, unsigned code) { command_codes_[s] = code; commands_.emplace_back(s, desc); return *this; } -void Command_line_parser::store_option(const vector &v) +void CommandLineParser::store_option(const vector &v, unsigned command) { if (v.size() == 0) return; - Option_base* o = nullptr; + OptionBase* o = nullptr; string id; vector v2; @@ -55,12 +50,12 @@ void Command_line_parser::store_option(const vector &v) throw runtime_error("Invalid option syntax."); else if (v[0].substr(0, 2) == "--") { id = v[0].substr(2); - map::const_iterator i = map_.find(id); + map::const_iterator i = map_.find(id); if (i != map_.end()) o = i->second; } else if (v[0][0] == '-') { id = v[0][1]; - map::const_iterator i = map_short_.find(v[0][1]); + map::const_iterator i = map_short_.find(v[0][1]); if (i != map_short_.end()) o = i->second; if (v[0].length() > 2) @@ -68,15 +63,19 @@ void Command_line_parser::store_option(const vector &v) } else throw runtime_error("Command line options must begin with - or --."); - if(o == nullptr || o->disabled) + if (o == nullptr || o->disabled) throw runtime_error("Invalid option: " + id); +#ifndef EXTRA + else if (find(o->group->commands.begin(), o->group->commands.end(), command) == o->group->commands.end()) + throw runtime_error("Option is not permitted for this workflow: " + id); +#endif else { v2.insert(v2.end(), v.begin() + 1, v.end()); o->read(v2); } } -void Command_line_parser::store(int count, const char ** str, unsigned &command) +void CommandLineParser::store(int count, const char ** str, unsigned &command) { if (count < 2) throw runtime_error("Syntax: diamond COMMAND [OPTIONS]. To print help message: diamond help"); @@ -88,21 +87,21 @@ void Command_line_parser::store(int count, const char ** str, unsigned &command) throw runtime_error("Invalid command: " + cmd + ". To print help message: diamond help"); command = it->second; - for (map::const_iterator i = map_.begin(); i != map_.end(); ++i) + for (map::const_iterator i = map_.begin(); i != map_.end(); ++i) i->second->set_default(); vector v; for (int i = 2; i < count; ++i) { if (str[i][0] == '-' && strlen(str[i]) > 1 && !isdigit(str[i][1])) { - store_option(v); + store_option(v, command); v.clear(); } v.push_back(str[i]); } - store_option(v); + store_option(v, command); } -void Command_line_parser::print_help() +void CommandLineParser::print_help() { static const size_t col1_width = 25; cout << "Syntax: diamond COMMAND [OPTIONS]" << endl << endl; @@ -111,20 +110,28 @@ void Command_line_parser::print_help() if (i->second != "") cout << i->first << '\t' << i->second << endl; cout << endl; - for (vector::const_iterator i = groups_.begin(); i != groups_.end(); ++i) { - if ((*i)->title == "") + for (const auto& g : groups_) { + if (g.title == "") continue; - cout << (*i)->title << ":" << endl; - for (vector::const_iterator j = (*i)->options.begin(); j != (*i)->options.end(); ++j) { - if((*j)->desc.empty()) + cout << g.title << ":" << endl; + for (const auto& option : g.options) { + if(option->desc.empty()) continue; - string col1 = "--" + (*j)->id; - if ((*j)->short_id) - col1 += string(" (-") + (*j)->short_id + ")"; + string col1 = "--" + option->id; + if (option->short_id) + col1 += string(" (-") + option->short_id + ")"; col1.append(max(col1_width, col1.length()) - col1.length(), ' '); - cout << col1 << (*j)->desc << endl; + cout << col1 << option->desc << endl; } cout << endl; } cout << "Online documentation at http://www.diamondsearch.org" << endl; } + +void CommandLineParser::require(const char* option) { + auto it = map_.find(option); + if (it == map_.end()) + throw std::runtime_error("Unknown option."); + if(!it->second->present()) + throw std::runtime_error("Missing parameter: --" + it->second->id + "/-" + it->second->short_id); +} \ No newline at end of file diff --git a/src/util/command_line_parser.h b/src/util/command_line_parser.h index 66eb56bfb..450042414 100644 --- a/src/util/command_line_parser.h +++ b/src/util/command_line_parser.h @@ -20,18 +20,19 @@ along with this program. If not, see . ****/ #pragma once +#include #include +#include #include #include #include #include #include #include -#include "ptr_vector.h" #include "options/option.h" -template -inline void read_option(_t &dst, const std::vector &v) +template +inline void read_option(T &dst, const std::vector &v) { throw std::runtime_error("Invalid option type."); } @@ -62,6 +63,12 @@ inline void read_option(long long& dst, const std::vector +inline void read_option>(Option& dst, const std::vector& v) +{ + dst = atoll(v[0].c_str()); +} + template<> inline void read_option(unsigned long long& dst, const std::vector& v) { @@ -74,10 +81,23 @@ inline void read_option(double &dst, const std::vector &v) dst = atof(v[0].c_str()); } + +template<> +inline void read_option>(Option &dst, const std::vector &v) +{ + dst = atof(v[0].c_str()); +} + template<> inline void read_option(std::string &dst, const std::vector &v) { dst = v[0]; } +template<> +inline void read_option>(Option &dst, const std::vector &v) +{ + dst = v[0]; +} + template<> inline void read_option>(std::vector &dst, const std::vector &v) { @@ -94,7 +114,7 @@ template<> inline void read_option(bool &dst, const std::vector &v) { dst = true; } -template +template inline bool check_pcount(const std::vector &v, const int min_count) { return v.size() == 1; @@ -118,92 +138,120 @@ inline bool check_pcount>>(const std::vector= (size_t)min_count; } +template +inline bool check_present(const T& v) { + return false; +} -struct Option_base -{ - Option_base(const std::string &id, char short_id, const std::string &desc, bool disabled) : - id(id), - desc(desc), - short_id (short_id), - disabled(disabled) - {} - virtual void read(const std::vector &v) = 0; - virtual void set_default() = 0; - virtual ~Option_base() - {} - const std::string id, desc; - const char short_id; - bool disabled; -}; +template<> +inline bool check_present(const std::string& v) { + return !v.empty(); +} + +template +inline void set_base_ptr(T& v, const OptionBase* ptr) { +} + +template +inline void set_base_ptr(Option& v, const OptionBase* ptr) { + v.base_ = ptr; +} + +template +inline void set_option_default(T& option, const T& value) { + option = value; +} + +template +inline void set_option_default(Option& option, const Option& value) { +} -template -struct OptionDesc : public Option_base +template +struct OptionDesc : public OptionBase { - OptionDesc(const char *id, char short_id, const char *desc, bool disabled, _t &store, _t def, const int min_count) : - Option_base(id, short_id, desc, disabled), + OptionDesc(const char *id, char short_id, const char *desc, bool disabled, T &store, T def, const int min_count, const OptionsGroup* group) : + OptionBase(id, short_id, desc, disabled, group), default_(def), min_count(min_count), - store_(store) + store_(&store) { } virtual void read(const std::vector &v) { - if (!check_pcount<_t>(v, min_count)) + if (!check_pcount(v, min_count)) throw std::runtime_error("Invalid parameter count for option '--" + id + "'"); - read_option(store_, v); + read_option(*store_, v); + } + virtual bool present() { + return check_present(*store_); } virtual void set_default() { - store_ = default_; + set_option_default(*store_, default_); } virtual ~OptionDesc() {} private: - const _t default_; + const T default_; const int min_count; - _t &store_; + T* store_; }; -struct Options_group +struct CommandLineParserBase +{ + std::map map_; + std::map map_short_; +}; + +struct OptionsGroup { struct Add_f { - Add_f(Options_group &parent): - parent_(parent) + Add_f(OptionsGroup& parent) : + parent_(&parent) {} - template - Add_f& operator()(const char *id, char short_id, const char *desc, _t &store, _t def = _t(), const int min_count = 1) + template + Add_f& operator()(const char* id, char short_id, const char* desc, T& store, T def = T(), const int min_count = 1) { - parent_.options.push_back(new OptionDesc<_t>(id, short_id, desc, parent_.disabled, store, def, min_count)); + auto o = new OptionDesc(id, short_id, desc, parent_->disabled, store, def, min_count, parent_); + parent_->options.emplace_back(o); + parent_->parent_->map_[id] = o; + parent_->parent_->map_short_[short_id] = o; + set_base_ptr(store, o); return *this; } private: - Options_group &parent_; + OptionsGroup* parent_; }; Add_f add() { return Add_f(*this); } - Options_group(const char *title, bool disabled = false): - title (title), - disabled(disabled) + OptionsGroup(const char* title, const std::vector& commands, bool disabled, CommandLineParserBase* parent) : + title(title), + commands(commands), + disabled(disabled), + parent_(parent) {} - PtrVector options; + std::list> options; std::string title; + const std::vector commands; bool disabled; +private: + CommandLineParserBase* parent_; }; -struct Command_line_parser +struct CommandLineParser : CommandLineParserBase { - Command_line_parser& add(const Options_group &group); - Command_line_parser& add_command(const char *s, const char *desc, unsigned code); + CommandLineParser& add_command(const char *s, const char *desc, unsigned code); void store(int count, const char **str, unsigned &command); + void require(const char* option); void print_help(); + OptionsGroup& add_group(const char *title, const std::vector& commands, bool disabled = false); + private: - void store_option(const std::vector &v); + void store_option(const std::vector &v, unsigned command); - std::map map_; - std::map map_short_; - std::vector groups_; + std::list groups_; std::map command_codes_; std::vector> commands_; }; \ No newline at end of file diff --git a/src/util/data_structures/array.h b/src/util/data_structures/array.h new file mode 100644 index 000000000..8226f72a4 --- /dev/null +++ b/src/util/data_structures/array.h @@ -0,0 +1,90 @@ +#pragma once +#include +#include + +template +struct Array { + + using Size = int64_t; + + Array(): + ptr_(nullptr), + size_(0) + {} + + Array(Size alloc_size) : + ptr_(new T[alloc_size]), + size_(0) + {} + + Array& operator=(Array&& a) noexcept { + ptr_ = a.ptr_; + size_ = a.size_; + a.ptr_ = nullptr; + return *this; + } + + ~Array() { + delete[] ptr_; + } + + T* data() { + return ptr_; + } + + T* begin() { + return ptr_; + } + + T* end() { + return ptr_ + size_; + } + + Size size() const { + return size_; + } + + void assign(const T& x) { + *ptr_ = x; + size_ = 1; + } + + template + void assign(It begin, It end) { + std::copy(begin, end, ptr_); + size_ = end - begin; + } + + template + void assign_reversed(It begin, It end) { + T* p = ptr_; + for (It i = end - 1; i >= begin; --i) + *p++ = *i; + size_ = end - begin; + } + + template + void push_back(It begin, It end) { + std::copy(begin, end, this->end()); + size_ += end - begin; + } + + template + void push_back_reversed(It begin, It end) { + T* p = this->end(); + for (It i = end - 1; i >= begin; --i) + *p++ = *i; + size_ += end - begin; + } + + void push_back(Size n, const T& value) { + T* p = end(); + std::fill(p, p + n, value); + size_ += n; + } + +private: + T* ptr_; + Size size_; + +}; \ No newline at end of file diff --git a/src/util/data_structures/bit_vector.h b/src/util/data_structures/bit_vector.h index 46a663068..d7ae80f1d 100644 --- a/src/util/data_structures/bit_vector.h +++ b/src/util/data_structures/bit_vector.h @@ -28,10 +28,13 @@ along with this program. If not, see . struct BitVector { - BitVector() {} + BitVector() : + size_(0) + {} BitVector(size_t n) : - data_((n + 63) / 64, 0) + data_((n + 63) / 64, 0), + size_(n) { } @@ -64,8 +67,21 @@ struct BitVector { return data_.empty(); } + int64_t size() const { + return size_; + } + + std::vector negative_list() const { + std::vector v; + for (int64_t i = 0; i < size_; ++i) + if (!get(i)) + v.push_back(i); + return v; + } + private: std::vector data_; + int64_t size_; }; \ No newline at end of file diff --git a/src/util/data_structures/compact_array.h b/src/util/data_structures/compact_array.h index 8040ade30..f291b3329 100644 --- a/src/util/data_structures/compact_array.h +++ b/src/util/data_structures/compact_array.h @@ -16,16 +16,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef COMPACT_ARRAY_H_ -#define COMPACT_ARRAY_H_ - +#pragma once #include #include #include "../io/deserializer.h" -using std::vector; - -template +template struct CompactArray { @@ -36,7 +32,7 @@ struct CompactArray limits_.reserve(size + 1); limits_.push_back(0); Deserializer d(data_.data(), data_.data() + data_.size(), Deserializer::VARINT); - _t x; + T x; for (size_t i = 0; i < size; ++i) { d >> x; size_t offset = d.data() - data_.data(); @@ -48,9 +44,9 @@ struct CompactArray throw std::runtime_error("Error loading CompactArray."); } - _t operator[](size_t i) const + T operator[](size_t i) const { - _t r; + T r; Deserializer(&data_[limits_[i]], &data_[limits_[i + 1]], Deserializer::VARINT) >> r; return r; } @@ -62,9 +58,7 @@ struct CompactArray private: - vector data_; - vector limits_; + std::vector data_; + std::vector limits_; }; - -#endif \ No newline at end of file diff --git a/src/util/data_structures/disjoint_set.h b/src/util/data_structures/disjoint_set.h new file mode 100644 index 000000000..b201f0f6f --- /dev/null +++ b/src/util/data_structures/disjoint_set.h @@ -0,0 +1,71 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once +#include +#include "flat_array.h" + +template +struct DisjointSet { + + DisjointSet(Int size) { + nodes_.reserve(size); + for (Int i = 0; i < size; ++i) + nodes_.emplace_back(i, 1); + } + + Int find(Int i) { + if (nodes_[i].parent != i) + return nodes_[i].parent = find(nodes_[i].parent); + else + return i; + } + + void merge(Int i, Int j) { + i = find(i); + j = find(j); + if (i == j) + return; + if (nodes_[i].size < nodes_[j].size) + std::swap(i, j); + nodes_[j].parent = i; + nodes_[i].size += nodes_[j].size; + } + + FlatArray sets(int threads) { + std::vector> v; + v.reserve(nodes_.size()); + for (Int i = 0; i < nodes_.size(); ++i) + v.emplace_back(find(i), i); + return make_flat_array(v.begin(), v.end(), threads).first; + } + +private: + + struct Node { + Node(Int parent, Int size) : + parent(parent), size(size) + {} + Int parent, size; + }; + + std::vector nodes_; + +}; \ No newline at end of file diff --git a/src/util/data_structures/double_array.h b/src/util/data_structures/double_array.h index 388ab7edd..48dd4a6be 100644 --- a/src/util/data_structures/double_array.h +++ b/src/util/data_structures/double_array.h @@ -24,7 +24,7 @@ along with this program. If not, see . #include #include "../range.h" -template +template struct DoubleArray { DoubleArray() @@ -53,11 +53,11 @@ struct DoubleArray { return *(uint32_t*)ptr_; } - Range<_t*> operator*() { - return { (_t*)(ptr_ + 4), (_t*)(ptr_ + 4) + count() }; + Range operator*() { + return { (T*)(ptr_ + 4), (T*)(ptr_ + 4) + count() }; } - Range<_t*>* operator->() { + Range* operator->() { range_ = this->operator*(); return &range_; } @@ -69,7 +69,7 @@ struct DoubleArray { } void next() { - ptr_ += count() * sizeof(_t) + 4; + ptr_ += count() * sizeof(T) + 4; } operator bool() const { @@ -80,7 +80,7 @@ struct DoubleArray { uint32_t n = count(); *(uint32_t*)(ptr_ + 4) = n; count() = 0; - ptr_ += n * sizeof(_t) + 4; + ptr_ += n * sizeof(T) + 4; } ptrdiff_t operator-(const Iterator &x) const { @@ -95,10 +95,10 @@ struct DoubleArray { void skip_del() { while (ptr_ < end_ && count() == 0) - ptr_ += (*(uint32_t*)(ptr_ + 4)) * sizeof(_t) + 4; + ptr_ += (*(uint32_t*)(ptr_ + 4)) * sizeof(T) + 4; } - Range<_t*> range_; + Range range_; char *ptr_, *end_; friend struct DoubleArray; @@ -126,8 +126,8 @@ struct DoubleArray { return uint32_t(it.ptr_ - data_); } - _t& operator[](uint32_t i) { - return *(_t*)(data_ + i); + T& operator[](uint32_t i) { + return *(T*)(data_ + i); } private: diff --git a/src/util/data_structures/flat_array.h b/src/util/data_structures/flat_array.h index 9ec1b88dd..b5cd5b23c 100644 --- a/src/util/data_structures/flat_array.h +++ b/src/util/data_structures/flat_array.h @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2020 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2020-2022 Max Planck Society for the Advancement of Science e.V. Code developed by Benjamin Buchfink @@ -20,23 +20,41 @@ along with this program. If not, see . #pragma once #include +#include +#define _REENTRANT +#include "../../lib/ips4o/ips4o.hpp" +#include "../algo/sort_helper.h" +#include "../algo/transform_iterator.h" +#include "../util.h" -template +template struct FlatArray { - typedef typename std::vector<_t>::iterator Iterator; - typedef typename std::vector<_t>::const_iterator ConstIterator; + typedef typename std::vector::iterator DataIterator; + typedef typename std::vector::const_iterator DataConstIterator; FlatArray() { limits_.push_back(0); } - void push_back(const _t &x) { + FlatArray(std::vector&& sizes): + data_(sum(sizes)), + limits_(sizes) + { + } + + FlatArray(std::vector&& limits, std::vector&& data) : + data_(std::move(data)), + limits_(std::move(limits)) + {} + + void push_back(const T &x) { data_.push_back(x); ++limits_.back(); } - void push_back(ConstIterator begin, ConstIterator end) { + template + void push_back(const It begin, const It end) { data_.insert(data_.end(), begin, end); limits_.push_back(limits_.back() + (end - begin)); } @@ -55,49 +73,230 @@ struct FlatArray { limits_.push_back(0); } - void reserve(size_t n) { - data_.reserve(n); + void shrink_to_fit() { + data_.shrink_to_fit(); + limits_.shrink_to_fit(); } - size_t size() const { + I size() const { return limits_.size() - 1; } - size_t data_size() const { + I data_size() const { return data_.size(); } - ConstIterator begin(size_t i) const { + DataConstIterator begin(I i) const { return data_.cbegin() + limits_[i]; } - ConstIterator end(size_t i) const { + DataConstIterator end(I i) const { return data_.cbegin() + limits_[i + 1]; } - ConstIterator cbegin(size_t i) const { + DataConstIterator cbegin(I i) const { return data_.cbegin() + limits_[i]; } - ConstIterator cend(size_t i) const { + DataConstIterator cend(I i) const { return data_.cbegin() + limits_[i + 1]; } - Iterator begin(size_t i) { + DataIterator begin(I i) { return data_.begin() + limits_[i]; } - Iterator end(size_t i) { + DataIterator end(I i) { return data_.begin() + limits_[i + 1]; } - size_t count(size_t i) const { + I count(I i) const { return limits_[i + 1] - limits_[i]; } + struct ConstIterator { + ConstIterator(typename std::vector::const_iterator limits, typename std::vector::const_iterator data_begin): + limits_(limits), + data_begin_(data_begin) + {} + DataConstIterator begin(size_t i) const { + return data_begin_ + limits_[i]; + } + DataConstIterator end(size_t i) const { + return data_begin_ + limits_[i + 1]; + } + int64_t operator-(const ConstIterator& i) const { + return limits_ - i.limits_; + } + private: + typename std::vector::const_iterator limits_; + typename std::vector::const_iterator data_begin_; + }; + + ConstIterator cbegin() const { + return ConstIterator(limits_.cbegin(), data_.cbegin()); + } + + ConstIterator cend() const { + return ConstIterator(limits_.cend() - 1, data_.cbegin()); + } + + struct Iterator { + Iterator() {} + Iterator(typename std::vector::const_iterator limits, typename std::vector::iterator data_begin) : + limits_(limits), + data_begin_(data_begin) + {} + DataIterator begin(I i) const { + return data_begin_ + limits_[i]; + } + DataIterator end(I i) const { + return data_begin_ + limits_[i + 1]; + } + int64_t operator-(const Iterator& i) const { + return limits_ - i.limits_; + } + private: + typename std::vector::const_iterator limits_; + typename std::vector::iterator data_begin_; + }; + + Iterator begin() { + return Iterator(limits_.cbegin(), data_.begin()); + } + + Iterator end() { + return Iterator(limits_.cend() - 1, data_.begin()); + } + + void reserve(const I size, const I data_size) { + data_.reserve(data_size); + limits_.reserve(size + 1); + } + + struct Range { + Range(DataConstIterator begin, DataConstIterator end) : + begin_(begin), + end_(end) + {} + DataConstIterator begin() const { + return begin_; + } + DataConstIterator end() const { + return end_; + } + private: + DataConstIterator begin_, end_; + }; + + Range operator[](I i) const { + return Range { cbegin(i), cend(i) }; + } + + I max_count() const { + I m = 0; + for (I i = 0; i < size(); ++i) + m = std::max(m, count(i)); + return m; + } + private: - std::vector<_t> data_; - std::vector limits_; + I sum(std::vector& sizes) { + assert(sizes.back() == 0); + I p = 0; + for (typename std::vector::iterator i = sizes.begin(); i < sizes.end(); ++i) { + const I size = *i; + *i = p; + p += size; + } + return p; + } + + std::vector data_; + std::vector limits_; + +}; -}; \ No newline at end of file +template +std::pair, std::vector> make_flat_array(const It begin, const It end, int num_threads) +{ + using Key = typename It::value_type::first_type; + using Value = typename It::value_type::second_type; +#if _MSC_FULL_VER == 191627045 || !defined(NDEBUG) + std:sort(begin, end); +#else + ips4o::parallel::sort(begin, end, std::less>(), num_threads); +#endif + auto it = merge_keys(begin, end, First()); + int64_t n = 0; + while (it.good()) { + ++n; + ++it; + } + std::pair, std::vector> r; + r.first.reserve(n, end - begin); + r.second.reserve(n); + auto it2 = merge_keys(begin, end, First()); + while (it2.good()) { + r.second.push_back(it2.key()); + r.first.push_back(transform(it2.begin(), Second()), transform(it2.end(), Second())); + ++it2; + } + return r; +} + + +template +FlatArray make_flat_array_dense(const It begin, const It end, int num_threads) +{ + using Key = typename It::value_type::first_type; + using Value = typename It::value_type::second_type; +#if _MSC_FULL_VER == 191627045 || !defined(NDEBUG) + std:sort(begin, end); +#else + ips4o::parallel::sort(begin, end, std::less>(), num_threads); +#endif + const Key max_key = (end - 1)->first; + FlatArray r; + r.reserve(max_key + 1, end - begin); + auto it = merge_keys(begin, end, First()); + Key k = 0; + while (it.good()) { + while (k != it.key()) { + r.next(); + ++k; + } + r.push_back(transform(it.begin(), Second()), transform(it.end(), Second())); + ++k; + ++it; + } + return r; +} + +template +FlatArray make_flat_array_dense(std::vector&& data, const typename T::Key key_end, int num_threads, GetKey get_key) +{ + std::vector limits; + limits.push_back(0); +#if _MSC_FULL_VER == 191627045 || !defined(NDEBUG) + std:sort(data.begin(), data.end()); +#else + ips4o::parallel::sort(data.begin(), data.end(), std::less(), num_threads); +#endif + limits.reserve(key_end + 1); + auto it = merge_keys(data.cbegin(), data.cend(), get_key); + typename T::Key k = 0; + while (it.good()) { + while (k != it.key()) { + limits.push_back(limits.back()); + ++k; + } + limits.push_back(limits.back() + it.count()); + ++k; + ++it; + } + while (k++ < key_end) + limits.push_back(limits.back()); + return FlatArray(std::move(limits), std::move(data)); +} \ No newline at end of file diff --git a/src/util/data_structures/hash_set.h b/src/util/data_structures/hash_set.h index 8289a53a0..0b40bd3eb 100644 --- a/src/util/data_structures/hash_set.h +++ b/src/util/data_structures/hash_set.h @@ -20,6 +20,7 @@ along with this program. If not, see . ****/ #pragma once +#include #include "../simd.h" struct Modulo2 {}; @@ -170,7 +171,7 @@ struct HashSet ++p; if (p == table + size_) { if (wrapped) - throw hash_table_overflow_exception(); + throw std::runtime_error("Hash table overflow"); p = table; wrapped = true; } diff --git a/src/util/data_structures/hash_table.h b/src/util/data_structures/hash_table.h index d278b2fda..5d298020e 100644 --- a/src/util/data_structures/hash_table.h +++ b/src/util/data_structures/hash_table.h @@ -16,35 +16,55 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef HASH_TABLE2_H_ -#define HASH_TABLE2_H_ - +#pragma once #include #include -template +struct Modulo { + size_t operator()(size_t offset, size_t size) const { + return offset % size; + } +}; + +struct NoModulo { + size_t operator()(size_t offset, size_t size) const { + return offset; + } +}; + +template struct HashTable : private _HashFunction { - struct Entry : public _V + struct Entry { _K key; + _V value; bool blank() const { - return _V::operator unsigned() == 0; + return value == (_V)0; } }; HashTable(size_t size, const _HashFunction &hash) : _HashFunction(hash), table((Entry*)calloc(size, sizeof(Entry))), - size_(size) + size_(size), + destroy_(true) + { + } + + HashTable(char* table, size_t size): + table((Entry*)table), + size_(size / sizeof(Entry)), + destroy_(false) { } ~HashTable() { - free(table); + if (destroy_) + free(table); } _V& operator[](_K key) @@ -93,8 +113,7 @@ struct HashTable : private _HashFunction Entry* get_entry(_K key, bool stat=false) { - //Entry *p = &table[_H()(key) % size_]; - Entry *p = &table[_HashFunction::operator()(key)]; + Entry *p = &table[ModuloOp()(_HashFunction::operator()(key), size_)]; bool wrapped = false; //if(stat) ++probe_n; while (p->key != key && !p->blank()) { @@ -112,7 +131,7 @@ struct HashTable : private _HashFunction Entry* get_present_entry(_K key, bool stat = false) { - Entry *p = &table[_HashFunction::operator()(key)]; + Entry *p = &table[ModuloOp()(_HashFunction::operator()(key), size_)]; bool wrapped = false; //if(stat) ++probe_n; while(true) { @@ -134,7 +153,7 @@ struct HashTable : private _HashFunction Entry* get_or_insert_entry(_K key, bool stat = false) { - Entry *p = &table[_HashFunction::operator()(key)]; + Entry *p = &table[ModuloOp()(_HashFunction::operator()(key), size_)]; bool wrapped = false; //if(stat) ++probe_n; while (true) { @@ -158,7 +177,6 @@ struct HashTable : private _HashFunction Entry *table; size_t size_; + bool destroy_; }; - -#endif \ No newline at end of file diff --git a/src/cluster/disjoint_set.h b/src/util/data_structures/lazy_disjoint_set.h similarity index 94% rename from src/cluster/disjoint_set.h rename to src/util/data_structures/lazy_disjoint_set.h index b142ceb6c..257cbf2ea 100644 --- a/src/cluster/disjoint_set.h +++ b/src/util/data_structures/lazy_disjoint_set.h @@ -81,7 +81,7 @@ template class LazyDisjointSet { virtual Node* get(const T i) = 0; virtual Node* get(const T* i) = 0; virtual uint64_t size() = 0; - virtual vector*>* getNodes() = 0; + virtual std::vector*>* getNodes() = 0; public: virtual ~LazyDisjointSet(){}; @@ -148,7 +148,7 @@ template class LazyDisjointIntegralSet : public LazyDisjointSet { std::vector*> nodes; virtual Node* get(T i){ - assert(i >= 0 && nodes.size() > i); + assert(i >= 0 && nodes.size() > (size_t)i); if(nodes[i] == nullptr){ nodes[i] = new IntegralNode(i); } @@ -162,7 +162,7 @@ template class LazyDisjointIntegralSet : public LazyDisjointSet { virtual uint64_t size(){ return nodes.size(); } - virtual vector*>* getNodes(){ + virtual std::vector*>* getNodes(){ return &nodes; } @@ -170,7 +170,7 @@ template class LazyDisjointIntegralSet : public LazyDisjointSet { static_assert(std::is_integral::value, "T needs to be an integral type"); LazyDisjointIntegralSet(T size) { - nodes = vector*>(size, nullptr); + nodes = std::vector*>(size, nullptr); } virtual ~LazyDisjointIntegralSet() { @@ -195,7 +195,7 @@ template class LazyDisjointIntegralSet : public LazyDisjointSet { std::vector> listOfSets(map.size()); for (Node* n : *(getNodes())) { if (n == nullptr) { - throw new runtime_error("In an integral set, we expect all elements to be initialized, see loop beofre.\n"); + throw new std::runtime_error("In an integral set, we expect all elements to be initialized, see loop beofre.\n"); } listOfSets[map[this->getRoot(n)->getValue()]].insert(*(n->getValue())); } @@ -225,7 +225,7 @@ template class LazyDisjointTypeSet : public LazyDisjointSet { virtual uint64_t size(){ return nodes.size(); } - virtual vector*>* getNodes(){ + virtual std::vector*>* getNodes(){ return &nodes; } diff --git a/src/util/data_structures/mem_buffer.h b/src/util/data_structures/mem_buffer.h index c79ec449b..d2ad5c986 100644 --- a/src/util/data_structures/mem_buffer.h +++ b/src/util/data_structures/mem_buffer.h @@ -21,12 +21,12 @@ along with this program. If not, see . #pragma once #include "../memory/alignment.h" -template +template struct MemBuffer { enum { ALIGN = 32 }; - typedef _t value_type; + typedef T value_type; MemBuffer(): data_(nullptr), @@ -35,7 +35,7 @@ struct MemBuffer { {} MemBuffer(size_t n): - data_((_t*)Util::Memory::aligned_malloc(n * sizeof(_t), ALIGN)), + data_((T*)Util::Memory::aligned_malloc(n * sizeof(T), ALIGN)), size_(n), alloc_size_(n) { @@ -48,7 +48,7 @@ struct MemBuffer { void resize(size_t n) { if (alloc_size_ < n) { Util::Memory::aligned_free(data_); - data_ = (_t*)Util::Memory::aligned_malloc(n * sizeof(_t), ALIGN); + data_ = (T*)Util::Memory::aligned_malloc(n * sizeof(T), ALIGN); alloc_size_ = n; } size_ = n; @@ -58,33 +58,33 @@ struct MemBuffer { return size_; } - _t* begin() { + T* begin() { return data_; } - _t* end() { + T* end() { return data_ + size_; } - const _t* begin() const { + const T* begin() const { return data_; } - const _t* end() const { + const T* end() const { return data_ + size_; } - _t& operator[](size_t i) { + T& operator[](size_t i) { return data_[i]; } - const _t& operator[](size_t i) const { + const T& operator[](size_t i) const { return data_[i]; } private: - _t *data_; + T *data_; size_t size_, alloc_size_; }; \ No newline at end of file diff --git a/src/util/data_structures/reorder_queue.h b/src/util/data_structures/reorder_queue.h new file mode 100644 index 000000000..69d4e75ef --- /dev/null +++ b/src/util/data_structures/reorder_queue.h @@ -0,0 +1,81 @@ +#pragma once +#include + +template +struct ReorderQueue +{ + ReorderQueue(size_t begin, F& f) : + f_(f), + begin_(begin), + next_(begin), + size_(0), + max_size_(0) + {} + + size_t size() const + { + return size_; + } + size_t max_size() const + { + return max_size_; + } + size_t next() const + { + return next_; + } + size_t begin() const { + return begin_; + } + + void push(size_t n, T value) + { + mtx_.lock(); + //cout << "n=" << n << " next=" << next_ << endl; + if (n != next_) { + backlog_[n] = value; + size_ += value ? value->alloc_size() : 0; + max_size_ = std::max(max_size_, size_); + mtx_.unlock(); + } + else + flush(value); + } + +private: + + void flush(T value) + { + size_t n = next_ + 1; + std::vector out; + out.push_back(value); + typename std::map::iterator i; + do { + while ((i = backlog_.begin()) != backlog_.end() && i->first == n) { + out.push_back(i->second); + backlog_.erase(i); + ++n; + } + mtx_.unlock(); + size_t size = 0; + for (typename std::vector::iterator j = out.begin(); j < out.end(); ++j) { + if (*j) { + f_(*j); + if (*j != value) + size += (*j)->alloc_size(); + delete *j; + } + } + out.clear(); + mtx_.lock(); + size_ -= size; + } while ((i = backlog_.begin()) != backlog_.end() && i->first == n); + next_ = n; + mtx_.unlock(); + } + + std::mutex mtx_; + F& f_; + std::map backlog_; + size_t begin_, next_, size_, max_size_; +}; \ No newline at end of file diff --git a/src/util/data_structures/sparse_flat_array.h b/src/util/data_structures/sparse_flat_array.h new file mode 100644 index 000000000..bc578d5cc --- /dev/null +++ b/src/util/data_structures/sparse_flat_array.h @@ -0,0 +1,190 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once +#include +#include +#include "flat_array.h" +#define _REENTRANT +#include "../../lib/ips4o/ips4o.hpp" +#include "../util.h" +#include "../algo/sort_helper.h" +#include "../algo/transform_iterator.h" + +template +struct SparseFlatArray { + + using DataConstIterator = typename FlatArray::DataConstIterator; + using MapIt = typename std::unordered_map::const_iterator; + + template + SparseFlatArray(const It begin, const It end, int num_threads) + { + ips4o::parallel::sort(begin, end, std::less>(), num_threads); + auto it = merge_keys(begin, end, First()); + int64_t n = 0; + while (it.good()) { + ++n; + ++it; + } + map_.reserve(n); + data_.reserve(n, end - begin); + auto it2 = merge_keys(begin, end, First()); + while (it2.good()) { + map_[it2.key()] = data_.size(); + data_.push_back(transform(it2.begin(), Second()), transform(it2.end(), Second())); + ++it2; + } + } + + bool empty() const { + return map_.empty(); + } + + int64_t size() const { + return map_.size(); + } + + int64_t data_size() const { + return data_.data_size(); + } + + struct Iterator { + Iterator(typename std::unordered_map::iterator it, FlatArray& data) : + it_(it), + data_(&data) + {} + Iterator operator++(int) { + Iterator it = *this; + ++it_; + return it; + } + bool operator==(const Iterator& it) const { + return it_ == it.it_; + } + bool operator!=(const Iterator& it) const { + return it_ != it.it_; + } + std::tuple::DataIterator, typename FlatArray::DataIterator> operator*() const { + return { it_->first, data_->begin(it_->second), data_->end(it_->second) }; + } + private: + typename std::unordered_map::iterator it_; + FlatArray* data_; + }; + + struct ConstIterator { + ConstIterator(MapIt it, const FlatArray& data): + it_(it), + data_(&data) + {} + ConstIterator operator++(int) { + Iterator it = *this; + ++it_; + return it; + } + bool operator==(const ConstIterator& it) const { + return it_ == it.it_; + } + bool operator!=(const ConstIterator& it) const { + return it_ != it.it_; + } + std::tuple::DataConstIterator, typename FlatArray::DataConstIterator> operator*() const { + return { it_->first, data_->cbegin(it_->second), data_->cend(it_->second) }; + } + private: + typename std::unordered_map::const_iterator it_; + const FlatArray* data_; + }; + + struct FlatIterator { + FlatIterator(MapIt map_it, MapIt map_end, const FlatArray& data) : + map_it_(map_it), + map_end_(map_end), + data_it_(map_it != map_end ? data.cbegin(map_it->second) : DataConstIterator()), + data_(&data) + {} + std::pair operator*() const { + return std::make_pair(map_it_->first, *data_it_); + } + FlatIterator& operator++() { + ++data_it_; + if (data_it_ >= data_->cend(map_it_->second)) { + ++map_it_; + if(map_it_ != map_end_) + data_it_ = data_->cbegin(map_it_->second); + } + return *this; + } + FlatIterator operator++(int) { + FlatIterator it = *this; + operator++(); + return it; + } + bool operator==(const FlatIterator& it) const { + return map_it_ == it.map_it_ && (data_it_ == it.data_it_ || map_it_ == map_end_); + } + bool operator!=(const FlatIterator& it) const { + return !(it == *this); + } + private: + typename std::unordered_map::const_iterator map_it_, map_end_; + DataConstIterator data_it_; + const FlatArray* data_; + }; + + Iterator begin() { + return Iterator(map_.begin(), data_); + } + + Iterator end() { + return Iterator(map_.end(), data_); + } + + ConstIterator cbegin() const { + return ConstIterator(map_.cbegin(), data_); + } + + ConstIterator cend() const { + return ConstIterator(map_.cend(), data_); + } + + DataConstIterator cbegin(Key key) const { + return data_.cbegin(map_.at(key)); + } + + DataConstIterator cend(Key key) const { + return data_.cend(map_.at(key)); + } + + FlatIterator flat_begin() const { + return FlatIterator(map_.cbegin(), map_.cend(), data_); + } + + FlatIterator flat_end() const { + return FlatIterator(map_.cend(), map_.cend(), data_); + } + +private: + + std::unordered_map map_; + FlatArray data_; + +}; \ No newline at end of file diff --git a/src/util/dynamic_iterator.h b/src/util/dynamic_iterator.h deleted file mode 100644 index 250803789..000000000 --- a/src/util/dynamic_iterator.h +++ /dev/null @@ -1,98 +0,0 @@ -/**** -DIAMOND protein aligner -Copyright (C) 2020 Max Planck Society for the Advancement of Science e.V. - -Code developed by Benjamin Buchfink - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -****/ - -#pragma once -#include -#include - -template -struct DynamicIterator { - - DynamicIterator(size_t count): - count(count) - {} - - virtual _t operator++(int) = 0; - virtual _t operator[](size_t i) = 0; - virtual ~DynamicIterator() {} - - const size_t count; - -}; - -template -struct VectorIterator : public DynamicIterator<_t> { - - VectorIterator(typename std::vector<_t>::const_iterator begin, typename std::vector<_t>::const_iterator end): - DynamicIterator<_t>(end - begin), - i_(0), - begin_(begin) - {} - - virtual _t operator++(int) override { - const size_t j = i_++; - if (j < DynamicIterator<_t>::count) - return begin_[j]; - else - return _t(); - } - - virtual _t operator[](size_t i) override { - return begin_[i]; - } - - virtual ~VectorIterator() {} - -private: - - std::atomic i_; - const typename std::vector<_t>::const_iterator begin_; - -}; - -template -struct ContainerIterator : public DynamicIterator<_t> { - - ContainerIterator(const _cont& container, size_t size) : - DynamicIterator<_t>(size), - container_(container), - i_(0) - {} - - virtual _t operator++(int) override { - const size_t j = i_++; - if (j < DynamicIterator<_t>::count) - return _t(container_[j], j); - else - return _t(); - } - - virtual _t operator[](size_t i) override { - return _t(container_[i], i); - } - - virtual ~ContainerIterator() {} - -private: - - const _cont& container_; - std::atomic i_; - -}; \ No newline at end of file diff --git a/src/util/enum.h b/src/util/enum.h index b6475509c..98feb8292 100644 --- a/src/util/enum.h +++ b/src/util/enum.h @@ -65,31 +65,33 @@ T from_string(const std::string& s) { return it->second.v; } -#define DEF_ENUM_FLAG_OPERATORS(T) constexpr inline T operator~ (T a) { return static_cast(~static_cast::type>(a)); } \ +#ifndef DEFINE_ENUM_FLAG_OPERATORS +#define DEFINE_ENUM_FLAG_OPERATORS(T) constexpr inline T operator~ (T a) { return static_cast(~static_cast::type>(a)); } \ constexpr inline T operator| (T a, T b) { return static_cast(static_cast::type>(a) | static_cast::type>(b)); } \ constexpr inline T operator& (T a, T b) { return static_cast(static_cast::type>(a) & static_cast::type>(b)); } \ constexpr inline T operator^ (T a, T b) { return static_cast(static_cast::type>(a) ^ static_cast::type>(b)); } \ inline T& operator|= (T& a, T b) { return reinterpret_cast(reinterpret_cast::type&>(a) |= static_cast::type>(b)); } \ inline T& operator&= (T& a, T b) { return reinterpret_cast(reinterpret_cast::type&>(a) &= static_cast::type>(b)); } \ inline T& operator^= (T& a, T b) { return reinterpret_cast(reinterpret_cast::type&>(a) ^= static_cast::type>(b)); } +#endif -template -bool flag_all(_t a, _t b) { - typedef typename std::underlying_type<_t>::type T; - T c = static_cast(b); - return (static_cast(a) & c) == c; +template +bool flag_all(T a, T b) { + typedef typename std::underlying_type::type U; + U c = static_cast(b); + return (static_cast(a) & c) == c; } -template -bool flag_any(_t a, _t b) { - typedef typename std::underlying_type<_t>::type T; - T c = static_cast(b); - return (static_cast(a) & c) != T(0); +template +bool flag_any(T a, T b) { + typedef typename std::underlying_type::type U; + U c = static_cast(b); + return (static_cast(a) & c) != U(0); } -template -bool flag_only(_t a, _t b) { - typedef typename std::underlying_type<_t>::type T; - T c = static_cast(b); - return (static_cast(a) & ~c) == T(0); +template +bool flag_only(T a, T b) { + typedef typename std::underlying_type::type U; + U c = static_cast(b); + return (static_cast(a) & ~c) == U(0); } diff --git a/src/util/escape_sequences.h b/src/util/escape_sequences.h index 0ddfe3fc6..53f6325c5 100644 --- a/src/util/escape_sequences.h +++ b/src/util/escape_sequences.h @@ -16,9 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef ESCAPE_SEQUENCES_H_ -#define ESCAPE_SEQUENCES_H_ - +#pragma once #include #include "text_buffer.h" @@ -93,6 +91,4 @@ inline void print_escaped(TextBuffer& buf, const std::string& s, const EscapeSeq esc->escape(s, tmp); buf << tmp; } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/basic/diagonal_segment.h b/src/util/geo/diagonal_segment.h similarity index 58% rename from src/basic/diagonal_segment.h rename to src/util/geo/diagonal_segment.h index 66df3c3b1..1d392217a 100644 --- a/src/basic/diagonal_segment.h +++ b/src/util/geo/diagonal_segment.h @@ -20,31 +20,36 @@ along with this program. If not, see . ****/ #pragma once -#include "translated_position.h" -#include "sequence.h" +#include "../../basic/translated_position.h" +#include "../../basic/sequence.h" -struct Diagonal_segment +struct DiagonalSegment { - Diagonal_segment() : - len(0) + DiagonalSegment() : + len(0), + score(0) {} - Diagonal_segment(int query_pos, int subject_pos, int len, int score) : + DiagonalSegment(int query_pos, int subject_pos, int len, int score, Loc ident = 0) : i(query_pos), j(subject_pos), len(len), - score(score) + score(score), + ident(ident) {} bool empty() const { return len == 0; } - interval query_range() const + Interval query_range() const { - return interval(i, i + len); + return Interval(i, i + len); } - interval subject_range() const + Interval subject_range() const { - return interval(j, j + len); + return Interval(j, j + len); + } + int subject_begin() const { + return j; } int subject_last() const { @@ -58,6 +63,9 @@ struct Diagonal_segment { return j + len; } + int query_begin() const { + return i; + } int query_end() const { return i + len; @@ -66,84 +74,102 @@ struct Diagonal_segment { return i - j; } - Diagonal_segment intersect(const Diagonal_segment &x) const + double id_percent() const { + return (double)ident / len * 100.0; + } + double cov_percent(Loc seq_len) const { + return (double)len / seq_len * 100.0; + } + Interval band_interval(const Loc band) const { + return { diag() - band, diag() + band }; + } + void set_query_end(Loc i) { + len = i - this->i; + } + void set_target_end(Loc j) { + len = j - this->j; + } + DiagonalSegment intersect(const DiagonalSegment&x) const { if (diag() != x.diag()) - return Diagonal_segment(); + return DiagonalSegment(); else { - const interval q = ::intersect(query_range(), x.query_range()); - return Diagonal_segment(q.begin_, ::intersect(subject_range(), x.subject_range()).begin_, q.length(), 0); + const Interval q = ::intersect(query_range(), x.query_range()); + return DiagonalSegment(q.begin_, ::intersect(subject_range(), x.subject_range()).begin_, q.length(), 0); } } - bool is_enveloped(const Diagonal_segment &x) const + bool is_enveloped(const DiagonalSegment&x) const { return score <= x.score && query_range().overlap_factor(x.query_range()) == 1 && subject_range().overlap_factor(x.subject_range()) == 1; } - Diagonal_segment transpose() const + DiagonalSegment transpose() const { - return Diagonal_segment(j, i, len, score); + return DiagonalSegment(j, i, len, score); } int partial_score(int diff) const { return score*std::max(len - diff, 0) / len; } - bool operator<=(const Diagonal_segment &rhs) const + bool operator<=(const DiagonalSegment&rhs) const { return i + len <= rhs.i && j + len <= rhs.j; } - bool operator==(const Diagonal_segment &rhs) const + bool operator==(const DiagonalSegment&rhs) const { return i == rhs.i && j == rhs.j && len == rhs.len; } - static bool cmp_subject(const Diagonal_segment &x, const Diagonal_segment &y) + static bool cmp_subject(const DiagonalSegment&x, const DiagonalSegment&y) { return x.j < y.j || (x.j == y.j && x.i < y.i); } - static bool cmp_score(const Diagonal_segment& x, const Diagonal_segment& y) + static bool cmp_score(const DiagonalSegment& x, const DiagonalSegment& y) { return x.score > y.score; } - static bool cmp_subject_end(const Diagonal_segment &x, const Diagonal_segment &y) + static bool cmp_subject_end(const DiagonalSegment&x, const DiagonalSegment&y) { return x.subject_end() < y.subject_end(); } - static bool cmp_heuristic(const Diagonal_segment &x, const Diagonal_segment &y) + static bool cmp_heuristic(const DiagonalSegment&x, const DiagonalSegment&y) { return (x.subject_end() < y.subject_end() && x.j < y.j) || (x.j - y.j < y.subject_end() - x.subject_end()); } - static bool cmp_diag(const Diagonal_segment &x, const Diagonal_segment &y) + static bool cmp_diag(const DiagonalSegment&x, const DiagonalSegment& y) { return x.diag() < y.diag() || (x.diag() == y.diag() && x.j < y.j); } - friend int abs_shift(const Diagonal_segment &x, const Diagonal_segment &y) + static bool cmp_len(const DiagonalSegment& x, const DiagonalSegment& y) { + return x.len > y.len; + } + friend int abs_shift(const DiagonalSegment&x, const DiagonalSegment& y) { return abs(x.diag() - y.diag()); } - friend std::ostream& operator<<(std::ostream &s, const Diagonal_segment &d) + friend std::ostream& operator<<(std::ostream &s, const DiagonalSegment& d) { s << "i=" << d.i << " j=" << d.j << " l=" << d.len << " score=" << d.score; return s; } - int i, j, len, score; + int i, j, len, score, ident; }; -struct DiagonalSegment +struct DiagonalSegmentT { - DiagonalSegment(): + DiagonalSegmentT(): len(0) {} - DiagonalSegment(const TranslatedPosition &i, int j, int len, int score=0): + DiagonalSegmentT(const TranslatedPosition &i, int j, int len, int score=0): i(i), j(j), len(len), score(score) {} - DiagonalSegment(const Diagonal_segment &d, Frame frame): + DiagonalSegmentT(const DiagonalSegment& d, Frame frame): i(TranslatedPosition(d.i, frame)), j(d.j), len(d.len), @@ -175,34 +201,34 @@ struct DiagonalSegment return i - j; } - friend std::ostream& operator<<(std::ostream &s, const DiagonalSegment &d) + friend std::ostream& operator<<(std::ostream &s, const DiagonalSegmentT &d) { s << "i=(" << d.i << ") j=" << d.j << " len=" << d.len << " score=" << d.score << std::endl; return s; } - interval query_absolute_range(int dna_len) const + Interval query_absolute_range(int dna_len) const { return TranslatedPosition::absolute_interval(i, i + len, dna_len); } - interval query_in_strand_range() const + Interval query_in_strand_range() const { - return interval(i.in_strand(), (i + len).in_strand()); + return Interval(i.in_strand(), (i + len).in_strand()); } - interval subject_range() const + Interval subject_range() const { - return interval(j, j + len); + return Interval(j, j + len); } - int partial_score(const DiagonalSegment &d) const + int partial_score(const DiagonalSegmentT &d) const { const double overlap = std::max(subject_range().overlap_factor(d.subject_range()), query_in_strand_range().overlap_factor(d.query_in_strand_range())); return int((1.0 - overlap)*score); } - void cut_out(const DiagonalSegment &d) + void cut_out(const DiagonalSegmentT &d) { const int ll = std::min(d.i.translated - i.translated, d.j - j), lr = std::min(query_end().translated - d.query_end().translated, subject_end() - d.subject_end()); @@ -225,3 +251,11 @@ struct DiagonalSegment TranslatedPosition i; int j, len, score; }; + +inline int diag_count(Loc query_len, Loc target_len) { + return query_len + target_len - 1; +} + +inline int diag_idx(Loc i, Loc j, Loc target_len) { + return i - j + target_len - 1; +} \ No newline at end of file diff --git a/src/util/geo/geo.h b/src/util/geo/geo.h new file mode 100644 index 000000000..3b0457600 --- /dev/null +++ b/src/util/geo/geo.h @@ -0,0 +1,38 @@ +#pragma once +#include "../../basic/value.h" + +namespace Geo { + +inline Loc i(Loc j, Loc d) { + return d + j; +} + +inline Loc j(Loc i, Loc d) { + return i - d; +} + +inline Loc diag_sub_matrix(Loc d, Loc i0, Loc j0) { + return d + j0 - i0; +} + +inline Loc rev_diag(Loc d, Loc qlen, Loc tlen) { + return -d + qlen - tlen; +} + +inline Loc min_diag(Loc qlen, Loc tlen) { + return -(tlen - 1); +} + +inline Loc max_diag(Loc qlen, Loc tlen) { + return qlen - 1; +} + +inline Loc clip_diag(Loc d, Loc qlen, Loc tlen) { + return std::min(std::max(d, min_diag(qlen, tlen)), max_diag(qlen, tlen)); +} + +inline void assert_diag_bounds(Loc d, Loc qlen, Loc tlen) { + assert(d >= min_diag(qlen, tlen) && d <= max_diag(qlen, tlen)); +} + +} \ No newline at end of file diff --git a/src/util/geo/hit.h b/src/util/geo/hit.h new file mode 100644 index 000000000..fd4c36d14 --- /dev/null +++ b/src/util/geo/hit.h @@ -0,0 +1,57 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once +#include +#include +#include "diagonal_segment.h" + +namespace Util { namespace Geometry { + +using Hit = std::pair; + +inline bool cmp_diag(const Hit& a, const Hit& b) { + const Loc d1 = a.first - a.second, d2 = b.first - b.second; + return d1 < d2 || (d1 == d2 && a.first < b.first); +} + +// Merges seed hits on one diagonal assumed to be sorted in ascending order. +template +inline std::vector merge_hits(const It begin, const It end, int32_t kmer_size, Loc window, Loc min_len) { + std::vector v; + if (begin == end) + return v; + Loc a = begin->first, b = begin->first + kmer_size, d = begin->first - begin->second; + for (It i = std::next(begin); i != end; ++i) { + if (i->first - b < window) + b = std::max(b, i->first + kmer_size); + else { + if (b - a >= min_len) + v.emplace_back(a, a - d, b - a, 0); + a = i->first; + b = i->first + kmer_size; + } + } + if (b - a >= min_len) + v.emplace_back(a, a - d, b - a, 0); + return v; +} + +}} diff --git a/src/util/interval.h b/src/util/geo/interval.h similarity index 54% rename from src/util/interval.h rename to src/util/geo/interval.h index 2837ede52..aa02f25cb 100644 --- a/src/util/interval.h +++ b/src/util/geo/interval.h @@ -18,15 +18,18 @@ along with this program. If not, see . #pragma once #include +#include #include +#include +#include "../../basic/value.h" -struct interval +struct Interval { - interval() : + Interval() : begin_(0), end_(0) { } - interval(int begin, int end) : + Interval(int begin, int end) : begin_(begin), end_(end) { } @@ -34,11 +37,11 @@ struct interval { return end_ > begin_ ? end_ - begin_ : 0; } - unsigned overlap(const interval &rhs) const + unsigned overlap(const Interval &rhs) const { return intersect(*this, rhs).length(); } - double overlap_factor(const interval &rhs) const + double overlap_factor(const Interval &rhs) const { return (double)overlap(rhs) / (double)length(); } @@ -46,28 +49,50 @@ struct interval { return p >= begin_ && p < end_; } - bool contains(const interval &i) const + bool contains(const Interval &i) const { return begin_ <= i.begin_ && end_ >= i.end_; } - friend std::ostream& operator<<(std::ostream &os, const interval &x) + friend std::ostream& operator<<(std::ostream &os, const Interval &x) { os << "[" << x.begin_ << ";" << x.end_ << "]"; return os; } - bool operator<(const interval &rhs) const + bool operator<(const Interval &rhs) const { return begin_ < rhs.begin_; } - void merge(const interval &k) + void merge(const Interval &k) { begin_ = std::min(begin_, k.begin_); end_ = std::max(end_, k.end_); } - friend interval intersect(const interval &lhs, const interval &rhs); + void check(int len) const { + if (begin_ < 0 || end_ < 0 || end_ < begin_ || begin_ >= len || end_ > len) + throw std::out_of_range(""); + } + friend Interval intersect(const Interval &lhs, const Interval &rhs); int begin_, end_; }; -inline interval intersect(const interval &lhs, const interval &rhs) +inline Interval intersect(const Interval &lhs, const Interval &rhs) { - return interval(std::max(lhs.begin_, rhs.begin_), std::min(lhs.end_, rhs.end_)); + return Interval(std::max(lhs.begin_, rhs.begin_), std::min(lhs.end_, rhs.end_)); } + +template +void make_disjoint(const It begin, const It end, Out out) { + if (begin == end) + return; + std::sort(begin, end); + Loc a = begin->begin_, b = begin->end_; + for (It i = std::next(begin); i != end; ++i) { + if (i->begin_ <= b) + b = std::max(b, i->end_); + else { + *out++ = Interval{ a,b }; + a = i->begin_; + b = i->end_; + } + } + *out = Interval{ a,b }; +} \ No newline at end of file diff --git a/src/util/interval_partition.h b/src/util/geo/interval_partition.h similarity index 72% rename from src/util/interval_partition.h rename to src/util/geo/interval_partition.h index 7c6c367ff..6b6a6d3d4 100644 --- a/src/util/interval_partition.h +++ b/src/util/geo/interval_partition.h @@ -16,9 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef INTERVAL_PARTITION_H_ -#define INTERVAL_PARTITION_H_ - +#pragma once #include #include #include @@ -28,23 +26,24 @@ struct IntervalNode { IntervalNode(): count(0), - min_score(INT_MAX), + min_score(std::numeric_limits::max()), max_score(0) {} - IntervalNode(int count, int min_score, int max_score): + IntervalNode(int64_t count, Score min_score, Score max_score): count(count), min_score(min_score), max_score(max_score) { } - IntervalNode add(int score, int cap) const + IntervalNode add(Score score, int64_t cap) const { return IntervalNode(count + 1, count < cap ? std::min(min_score, score) : min_score, std::max(max_score, score)); } - int count, min_score, max_score; + int64_t count; + Score min_score, max_score; }; -struct IntervalPartition : protected std::map +struct IntervalPartition : protected std::map { struct MaxScore {}; @@ -69,30 +68,30 @@ struct IntervalPartition : protected std::map ++j_; return *this; } - std::pair operator*() const + std::pair operator*() const { - return std::make_pair(interval(i_->first, j_ == parent_.end() ? INT_MAX : j_->first), i_->second); + return std::make_pair(Interval(i_->first, j_ == parent_.end() ? INT_MAX : j_->first), i_->second); } private: const_iterator i_, j_; const IntervalPartition &parent_; }; - IntervalPartition(int cap): + IntervalPartition(int64_t cap): cap(cap) { (*this)[0] = IntervalNode(); } - void insert(interval k, int score) + void insert(Interval k, Score score) { iterator i = lower_bound(k.begin_); if (i == end()) - i = std::map::insert(std::make_pair(k.begin_, IntervalNode())).first; + i = std::map::insert(std::make_pair(k.begin_, IntervalNode())).first; else if (i->first != k.begin_) { //assert(i != std::map::begin()); i--; - i = std::map::insert(std::make_pair(k.begin_, i->second)).first; + i = std::map::insert(std::make_pair(k.begin_, i->second)).first; } IntervalNode last; while (i != end() && i->first < k.end_) { @@ -104,10 +103,10 @@ struct IntervalPartition : protected std::map (*this)[k.end_] = last; } - int covered(interval k) const + Loc covered(Interval k) const { Iterator i = begin(k.begin_); - std::pair l; + std::pair l; int c = 0; while (i.good() && (l = *i).first.begin_ < k.end_) { if (l.second.count >= cap) @@ -117,10 +116,10 @@ struct IntervalPartition : protected std::map return c; } - int covered(interval k, int max_score, const MaxScore&) const + Loc covered(Interval k, Score max_score, const MaxScore&) const { Iterator i = begin(k.begin_); - std::pair l; + std::pair l; int c = 0; while (i.good() && (l = *i).first.begin_ < k.end_) { if (l.second.max_score >= max_score) @@ -130,11 +129,11 @@ struct IntervalPartition : protected std::map return c; } - int covered(interval k, int min_score, const MinScore&) const + Loc covered(Interval k, Score min_score, const MinScore&) const { Iterator i = begin(k.begin_); - std::pair l; - int c = 0; + std::pair l; + Loc c = 0; while (i.good() && (l = *i).first.begin_ < k.end_) { if (l.second.count >= cap && l.second.min_score >= min_score) c += k.overlap(l.first); @@ -143,11 +142,11 @@ struct IntervalPartition : protected std::map return c; } - int min_score(interval k) const + Score min_score(Interval k) const { Iterator i = begin(k.begin_); - std::pair l; - int s = INT_MAX; + std::pair l; + Score s = std::numeric_limits::max(); while (i.good() && (l = *i).first.begin_ < k.end_) { if (l.second.count < cap) return 0; @@ -157,16 +156,16 @@ struct IntervalPartition : protected std::map return s; } - int max_score(interval k) const + Score max_score(Interval k) const { Iterator i = begin(k.begin_); - std::pair l; - int s = INT_MAX; + std::pair l; + Score s = std::numeric_limits::max(); while (i.good() && (l = *i).first.begin_ < k.end_) { s = std::min(s, l.second.max_score); ++i; } - assert(s != INT_MAX); + assert(s != std::numeric_limits::max()); return s; } @@ -185,8 +184,6 @@ struct IntervalPartition : protected std::map return Iterator(i, j, *this); } - const int cap; + const int64_t cap; }; - -#endif \ No newline at end of file diff --git a/src/util/geo/interval_tree.h b/src/util/geo/interval_tree.h new file mode 100644 index 000000000..6f0c95584 --- /dev/null +++ b/src/util/geo/interval_tree.h @@ -0,0 +1,57 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once +#include "../../lib/interval_tree/interval_tree.hpp" +#include "interval.h" + +template +struct IntervalTree { + + bool is_overlapped(Interval i, double overlap) { + Overlaps o(i, Int(i.length() * overlap)); + tree_.overlap_find_all({ i.begin_, i.end_ }, o); + return o.n >= o.max; + } + + void insert(Interval i) { + tree_.insert_overlap({ i.begin_, i.end_ }); + } + +private: + + struct Overlaps { + Overlaps(Interval i, Int max) : + i(i), + max(max), + n(0) + {} + bool operator()(typename lib_interval_tree::interval_tree_t::iterator it) { + n += intersect(Interval(it->interval().low(), it->interval().high()), i).length(); + return n < max; + } + const Interval i; + const Int max; + Int n; + }; + + lib_interval_tree::interval_tree_t tree_; + +}; \ No newline at end of file diff --git a/src/util/hash_function.h b/src/util/hash_function.h index b336e0a40..b19efadeb 100644 --- a/src/util/hash_function.h +++ b/src/util/hash_function.h @@ -16,10 +16,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef HASH_FUNCTION_H_ -#define HASH_FUNCTION_H_ - -struct murmur_hash { +#pragma once +struct MurmurHash { uint64_t operator()(uint64_t h) const { h ^= h >> 33; @@ -31,4 +29,3 @@ struct murmur_hash { } }; -#endif /* HASH_FUNCTION_H_ */ diff --git a/src/util/hash_table.h b/src/util/hash_table.h deleted file mode 100644 index ad9bdaa0d..000000000 --- a/src/util/hash_table.h +++ /dev/null @@ -1,130 +0,0 @@ -/**** -DIAMOND protein aligner -Copyright (C) 2013-2017 Benjamin Buchfink - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -****/ - -#pragma once -#include -#include -#include -#include -#include -#include "hash_function.h" -#include "simd.h" - -struct hash_table_overflow_exception : public std::exception -{ - virtual const char* what() const throw() - { - return "Hash table overflow"; - } -}; - -template struct value_compare -{ -bool operator()(T x) const -{ - return x == value; -} -}; - -template class PHash_table -{ - -public: - - struct entry - { - uint16_t finger_print; - _V value; - }; // __attribute__((packed)); - - PHash_table() : - size_(0) - {} - - PHash_table(size_t size) : - table(new entry[size]), - size_(size) - { - memset(table.get(), 0, size_ * sizeof(entry)); - } - - PHash_table(size_t size, double factor) : - size_(std::max(size_t((double)size * factor), (size_t)1llu)), - table(new entry[size_]) - { - memset(table.get(), 0, size_ * sizeof(entry)); - } - - entry* operator[](uint64_t hash) const - { - entry *entry = get_entry(hash); - if (entry->value == 0u) - return NULL; - return entry; - } - - entry* insert(uint64_t hash) - { - entry *entry = get_entry(hash); - if (entry->value == 0u) - entry->finger_print = finger_print(hash); - return entry; - } - - size_t size() const - { - return size_; - } - - size_t count() const - { - size_t n(0); - for (size_t i = 0; i> 16) % size_); - const uint16_t fp = finger_print(hash); - bool wrapped = false; - while (p->finger_print != fp && p->value != 0u) { - ++p; - if (p == table.get() + size_) { - if (wrapped) - throw hash_table_overflow_exception(); - p = table.get(); - wrapped = true; - } - } - return p; - } - - size_t size_; - std::unique_ptr table; - -}; diff --git a/src/util/hsp/approx_hsp.h b/src/util/hsp/approx_hsp.h new file mode 100644 index 000000000..79f3ec7f5 --- /dev/null +++ b/src/util/hsp/approx_hsp.h @@ -0,0 +1,137 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2016-2022 Max Planck Society for the Advancement of Science e.V. + Benjamin Buchfink + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once +#include +#include +#include +#include "../util/geo/interval.h" +#include "../util/geo/diagonal_segment.h" + +struct Anchor : public DiagonalSegment { + Anchor(): + DiagonalSegment(), + prefix_score(0), + d_min_left(std::numeric_limits::max()), d_max_left(std::numeric_limits::min()), d_min_right(std::numeric_limits::max()), d_max_right(std::numeric_limits::min()) + {} + Anchor(const DiagonalSegment& d) : + DiagonalSegment(d), + prefix_score(0), + d_min_left(std::numeric_limits::max()), d_max_left(std::numeric_limits::min()), d_min_right(std::numeric_limits::max()), d_max_right(std::numeric_limits::min()) + { + } + Anchor(const DiagonalSegment& d, Loc d_min_left, Loc d_max_left, Loc d_min_right, Loc d_max_right, Score prefix_score) : + DiagonalSegment(d), + prefix_score(prefix_score), + d_min_left(d_min_left), d_max_left(d_max_left), d_min_right(d_min_right), d_max_right(d_max_right) + { + } + Anchor(int query_pos, int subject_pos, int len, int score, Loc ident = 0) : + DiagonalSegment(query_pos, subject_pos, len, score, ident), + prefix_score(0), + d_min_left(std::numeric_limits::max()), d_max_left(std::numeric_limits::min()), d_min_right(std::numeric_limits::max()), d_max_right(std::numeric_limits::min()) + { + } + Anchor& operator=(const DiagonalSegment& d) { + *(DiagonalSegment*)this = d; + return *this; + } + Score prefix_score; + Loc d_min_left, d_max_left, d_min_right, d_max_right; +}; + +struct ApproxHsp +{ + ApproxHsp(unsigned frame, Score score = 0) : + d_min(std::numeric_limits::max()), + d_max(std::numeric_limits::min()), + score(score), + frame((int)frame) + {} + ApproxHsp(const Interval &query_source_range) : + query_source_range(query_source_range) + {} + ApproxHsp(int d_min, int d_max, int score, int frame, const Interval& query_range, const Interval& subject_range, const Anchor& max_diag, double evalue = DBL_MAX): + d_min(d_min), + d_max(d_max), + score(score), + frame(frame), + query_range(query_range), + subject_range(subject_range), + evalue(evalue), + max_diag(max_diag) + {} + int partial_score(const DiagonalSegment &d) const + { + const double overlap = std::max(d.subject_range().overlap_factor(subject_range), d.query_range().overlap_factor(query_range)); + return int((1 - overlap)*d.score); + } + int partial_score(const ApproxHsp &x) const + { + const double overlap = std::max(x.subject_range.overlap_factor(subject_range), x.query_range.overlap_factor(query_range)); + return int((1 - overlap)*x.score); + } + bool disjoint(const DiagonalSegment &d) const + { + return intersect(query_range, d.query_range()).length() == 0 && intersect(subject_range, d.subject_range()).length() == 0; + } + bool disjoint(const ApproxHsp &x) const + { + return intersect(query_range, x.query_range).length() == 0 && intersect(subject_range, x.subject_range).length() == 0; + } + bool rel_disjoint(const DiagonalSegment &d) const + { + return intersect(query_range, d.query_range()).length() == 0 || intersect(subject_range, d.subject_range()).length() == 0; + } + bool rel_disjoint(const ApproxHsp &x) const + { + return intersect(query_range, x.query_range).length() == 0 || intersect(subject_range, x.subject_range).length() == 0; + } + bool collinear(const ApproxHsp &x) const + { + const int di = x.query_range.begin_ - query_range.begin_, dj = x.subject_range.begin_ - subject_range.begin_; + return (di >= 0 && dj >= 0) || (di <= 0 && dj <= 0); + } + bool collinear(const DiagonalSegment &d) const + { + const int di = d.i - query_range.begin_, dj = d.j - subject_range.begin_; + return (di >= 0 && dj >= 0) || (di <= 0 && dj <= 0); + } + static bool cmp_diag(const ApproxHsp& x, const ApproxHsp& y) + { + return x.frame < y.frame || (x.frame == y.frame && x.d_min < y.d_min); + } + friend std::ostream& operator<<(std::ostream& s, const ApproxHsp& h) { + s << "Score=" << h.score << " query_range=" << h.query_range << " target_range=" << h.subject_range << std::endl; + return s; + } + struct Frame + { + unsigned operator()(const ApproxHsp &x) const + { + return x.frame; + } + }; + int d_min, d_max, score, frame; + Interval query_source_range, query_range, subject_range; + double evalue; + Anchor max_diag; +}; diff --git a/src/util/intrin.h b/src/util/intrin.h index 4b25d6e70..000c2b868 100644 --- a/src/util/intrin.h +++ b/src/util/intrin.h @@ -16,9 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef INTRIN_H_ -#define INTRIN_H_ - +#pragma once #include #ifdef _MSC_VER #include @@ -85,6 +83,3 @@ static inline int clz(uint32_t x) { return __builtin_clz(x); #endif } - - -#endif \ No newline at end of file diff --git a/src/util/io/compressed_stream.cpp b/src/util/io/compressed_stream.cpp index 90d76ff11..de113581d 100644 --- a/src/util/io/compressed_stream.cpp +++ b/src/util/io/compressed_stream.cpp @@ -19,6 +19,8 @@ along with this program. If not, see . #include #include "compressed_stream.h" +using std::pair; + void ZlibSource::init() { eos_ = false; @@ -75,6 +77,7 @@ void ZlibSource::close() void ZlibSource::rewind() { prev_->rewind(); + inflateEnd(&strm); init(); } diff --git a/src/util/io/deserializer.cpp b/src/util/io/deserializer.cpp index 2ddb819cc..83720ea38 100644 --- a/src/util/io/deserializer.cpp +++ b/src/util/io/deserializer.cpp @@ -49,7 +49,7 @@ void Deserializer::rewind() begin_ = end_ = nullptr; } -Deserializer& Deserializer::seek(size_t pos) +Deserializer& Deserializer::seek(int64_t pos) { if (buffer_->seekable() && buffer_->tell()) { const size_t d = buffer_->tell() - pos; @@ -58,7 +58,7 @@ Deserializer& Deserializer::seek(size_t pos) return *this; } } - buffer_->seek(pos); + buffer_->seek(pos, SEEK_SET); begin_ = end_ = nullptr; return *this; } diff --git a/src/util/io/deserializer.h b/src/util/io/deserializer.h index 13c3d6b5e..80217dba3 100644 --- a/src/util/io/deserializer.h +++ b/src/util/io/deserializer.h @@ -38,7 +38,7 @@ struct Deserializer Deserializer(StreamEntity* buffer); void rewind(); - Deserializer& seek(size_t pos); + Deserializer& seek(int64_t pos); void seek_forward(size_t n); bool seek_forward(char delimiter); void close(); @@ -61,19 +61,36 @@ struct Deserializer return *this; } - Deserializer& operator>>(int &x) + Deserializer& operator>>(int32_t &x) + { + if (varint) { + uint32_t i; + read_varint(*this, i); + x = (int32_t)i; + } + else { + read(x); + x = big_endian_byteswap(x); + } + return *this; + } + + Deserializer& operator>>(int64_t& x) { read(x); + x = big_endian_byteswap(x); return *this; } Deserializer& operator>>(short& x) { read(x); + x = big_endian_byteswap(x); return *this; } Deserializer& operator>>(unsigned short& x) { read(x); + x = big_endian_byteswap(x); return *this; } @@ -120,13 +137,13 @@ struct Deserializer return *this; } - Deserializer& operator>>(std::vector &v) + Deserializer& operator>>(std::vector &v) { - uint32_t n, x; + int32_t n, x; *this >> n; v.clear(); v.reserve(n); - for (unsigned i = 0; i < n; ++i) { + for (int32_t i = 0; i < n; ++i) { *this >> x; v.push_back(x); } diff --git a/src/util/io/exceptions.h b/src/util/io/exceptions.h index 20fa9e285..ea4508dad 100644 --- a/src/util/io/exceptions.h +++ b/src/util/io/exceptions.h @@ -16,9 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef EXCEPTIONS_H_ -#define EXCEPTIONS_H_ - +#pragma once #include #include @@ -29,9 +27,9 @@ struct UnsupportedOperation : public std::runtime_error { } }; -struct File_open_exception : public std::runtime_error +struct FileOpenException : public std::runtime_error { - File_open_exception(const std::string &file_name) : + FileOpenException(const std::string &file_name) : std::runtime_error(std::string("Error opening file " + file_name)) { } }; @@ -62,6 +60,4 @@ struct StreamReadException : public std::runtime_error StreamReadException(size_t line_count, const char *msg) : runtime_error(std::string("Error reading input stream at line ") + std::to_string(line_count) + ": " + msg) {} -}; - -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/src/util/io/file_sink.cpp b/src/util/io/file_sink.cpp index 7627c53b7..352d0eb21 100644 --- a/src/util/io/file_sink.cpp +++ b/src/util/io/file_sink.cpp @@ -30,6 +30,7 @@ along with this program. If not, see . #include #include #include +#include #endif #include "file_sink.h" @@ -39,6 +40,20 @@ using std::endl; using std::string; using std::runtime_error; +static const char* msg = "\nError opening file "; + +#ifndef _MSC_VER +static int posix_flags(const char* mode) { + if (strcmp(mode, "wb") == 0) + return O_WRONLY | O_CREAT | O_TRUNC; + else if (strcmp(mode, "r+b") == 0) + return O_RDWR; + else if (strcmp(mode, "w+b") == 0) + return O_RDWR | O_CREAT | O_TRUNC; + throw std::runtime_error("Invalid fopen mode."); +} +#endif + FileSink::FileSink(const string &file_name, const char *mode, bool async, size_t buffer_size): file_name_(file_name), async_(async) @@ -46,16 +61,16 @@ FileSink::FileSink(const string &file_name, const char *mode, bool async, size_t #ifdef _MSC_VER f_ = file_name.length() == 0 ? stdout : fopen(file_name.c_str(), mode); #else - int fd_ = file_name.length() == 0 ? 1 : POSIX_OPEN(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + int fd_ = file_name.length() == 0 ? 1 : POSIX_OPEN(file_name.c_str(), posix_flags(mode), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (fd_ < 0) { - perror(0); - throw File_open_exception(file_name_); + perror((msg + file_name).c_str()); + throw FileOpenException(file_name_); } f_ = fdopen(fd_, mode); #endif if (f_ == 0) { - perror(0); - throw File_open_exception(file_name); + perror((msg + file_name).c_str()); + throw FileOpenException(file_name); } if (buffer_size != 0) if (setvbuf(f_, nullptr, _IOFBF, buffer_size) != 0) @@ -69,8 +84,8 @@ FileSink::FileSink(const string &file_name, int fd, const char *mode, bool async async_(async) { if (f_ == 0) { - perror(0); - throw File_open_exception(file_name); + perror((msg + file_name).c_str()); + throw FileOpenException(file_name); } if (buffer_size != 0) if (setvbuf(f_, nullptr, _IOFBF, buffer_size) != 0) @@ -101,22 +116,22 @@ void FileSink::write(const char *ptr, size_t count) if (async_) mtx_.unlock(); } -void FileSink::seek(size_t p) +void FileSink::seek(int64_t p, int origin) { #ifdef _MSC_VER - if (_fseeki64(f_, (int64_t)p, SEEK_SET) != 0) { + if (_fseeki64(f_, p, origin) != 0) { perror(0); throw std::runtime_error("Error calling fseek."); } #else - if (fseek(f_, p, SEEK_SET) != 0) { + if (fseek(f_, p, origin) != 0) { perror(0); throw std::runtime_error("Error calling fseek."); } #endif } -size_t FileSink::tell() +int64_t FileSink::tell() { #ifdef _MSC_VER int64_t x; diff --git a/src/util/io/file_sink.h b/src/util/io/file_sink.h index 4525d1f52..a9fe9d434 100644 --- a/src/util/io/file_sink.h +++ b/src/util/io/file_sink.h @@ -26,30 +26,28 @@ along with this program. If not, see . #include "stream_entity.h" #include "exceptions.h" -using std::string; - struct FileSink : public StreamEntity { - FileSink(const string &file_name, const char *mode = "wb", bool async = false, size_t buffer_size = 0); + FileSink(const std::string &file_name, const char *mode = "wb", bool async = false, size_t buffer_size = 0); #ifndef _MSC_VER - FileSink(const string &file_name, int fd, const char *mode, bool async = false, size_t buffer_size = 0); + FileSink(const std::string &file_name, int fd, const char *mode, bool async = false, size_t buffer_size = 0); #endif - virtual void close(); - virtual void write(const char *ptr, size_t count); - virtual void seek(size_t p); - virtual void rewind(); - virtual size_t tell(); - virtual const string& file_name() const + virtual void close() override; + virtual void write(const char *ptr, size_t count) override; + virtual void seek(int64_t p, int origin = SEEK_SET) override; + virtual void rewind() override; + virtual int64_t tell() override; + virtual const std::string& file_name() const override { return file_name_; } - virtual FILE* file() + virtual FILE* file() override { return f_; } protected: FILE *f_; - const string file_name_; + const std::string file_name_; std::mutex mtx_; const bool async_; friend struct FileSource; diff --git a/src/util/io/file_source.cpp b/src/util/io/file_source.cpp index 476b50dfb..c1b1a56e6 100644 --- a/src/util/io/file_source.cpp +++ b/src/util/io/file_source.cpp @@ -41,6 +41,7 @@ FileSource::FileSource(const string &file_name) : StreamEntity(true), file_name_(file_name) { + static const char* msg = "\nError opening file "; const bool is_stdin = file_name.empty() || file_name == "-"; #ifdef _MSC_VER f_ = is_stdin ? stdin : fopen(file_name.c_str(), "rb"); @@ -48,22 +49,22 @@ FileSource::FileSource(const string &file_name) : struct stat buf; if (!is_stdin && stat(file_name.c_str(), &buf) < 0) { - perror(0); - throw std::runtime_error(string("Error calling stat on file ") + file_name); + perror((msg + file_name).c_str()); + throw FileOpenException(file_name); } if (is_stdin || !S_ISREG(buf.st_mode)) seekable_ = false; int fd_ = is_stdin ? 0 : POSIX_OPEN2(file_name.c_str(), O_RDONLY); if (fd_ < 0) { - perror(0); - throw std::runtime_error(string("Error opening file ") + file_name); + perror((msg + file_name).c_str()); + throw FileOpenException(file_name); } f_ = fdopen(fd_, "rb"); #endif if (f_ == 0) { - perror(0); - throw File_open_exception(file_name); + perror((msg + file_name).c_str()); + throw FileOpenException(file_name); } } @@ -79,10 +80,10 @@ void FileSource::rewind() ::rewind(f_); } -void FileSource::seek(size_t pos) +void FileSource::seek(int64_t pos, int origin) { #ifdef _MSC_VER - if (_fseeki64(f_, (int64_t)pos, SEEK_SET) != 0) { + if (_fseeki64(f_, pos, SEEK_SET) != 0) { perror(0); throw std::runtime_error("Error executing seek on file " + file_name_); } @@ -142,7 +143,7 @@ void FileSource::close() } } -size_t FileSource::tell() +int64_t FileSource::tell() { #ifdef _MSC_VER int64_t x; diff --git a/src/util/io/file_source.h b/src/util/io/file_source.h index afd3630ca..d5719bc40 100644 --- a/src/util/io/file_source.h +++ b/src/util/io/file_source.h @@ -16,22 +16,20 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef FILE_SOURCE_H_ -#define FILE_SOURCE_H_ - +#pragma once #include "stream_entity.h" struct FileSource : public StreamEntity { - FileSource(const string &file_name); - FileSource(const string &file_name, FILE *file); + FileSource(const std::string &file_name); + FileSource(const std::string &file_name, FILE *file); virtual void rewind() override; - virtual void seek(size_t pos) override; + virtual void seek(int64_t p, int origin) override; virtual void seek_forward(size_t n) override; - virtual size_t tell() override; + virtual int64_t tell() override; virtual size_t read(char *ptr, size_t count) override; virtual void close() override; - virtual const string& file_name() const override + virtual const std::string& file_name() const override { return file_name_; } @@ -44,7 +42,5 @@ struct FileSource : public StreamEntity {} protected: FILE *f_; - const string file_name_; -}; - -#endif \ No newline at end of file + const std::string file_name_; +}; \ No newline at end of file diff --git a/src/util/io/input_file.cpp b/src/util/io/input_file.cpp index da26bd4a3..4c2bcf6fd 100644 --- a/src/util/io/input_file.cpp +++ b/src/util/io/input_file.cpp @@ -36,6 +36,8 @@ along with this program. If not, see . #include "zstd_stream.h" #endif +using std::string; + static Compressor detect_compressor(const char* b) { if ((b[0] == '\x1F' && b[1] == '\x8B') // gzip header || (b[0] == '\x78' && (b[1] == '\x01' // zlib header @@ -65,7 +67,8 @@ static StreamEntity* make_decompressor(const Compressor c, StreamEntity* buffer) InputFile::InputFile(const string &file_name, int flags) : Deserializer(new InputStreamBuffer(new FileSource(file_name), flags)), file_name(file_name), - unlinked(false) + unlinked(false), + temp_file(false) { if (file_name.empty() || file_name == "-") return; @@ -98,7 +101,17 @@ InputFile::InputFile(const string &file_name, int flags) : InputFile::InputFile(TempFile &tmp_file, int flags) : Deserializer(new InputStreamBuffer(new FileSource(tmp_file.file_name(), tmp_file.file()), flags)), file_name(tmp_file.file_name()), - unlinked(tmp_file.unlinked) + unlinked(tmp_file.unlinked), + temp_file(true) +{ + tmp_file.rewind(); +} + +InputFile::InputFile(OutputFile& tmp_file, int flags) : + Deserializer(new InputStreamBuffer(new FileSource(tmp_file.file_name(), tmp_file.file()), flags)), + file_name(tmp_file.file_name()), + unlinked(false), + temp_file(true) { tmp_file.rewind(); } @@ -117,7 +130,7 @@ uint64_t InputFile::hash() { char h[16]; std::fill(h, h + 16, '\0'); while ((n = read_raw(buf, SIZE)) > 0) - MurmurHash3_x64_128(buf, n, h, h); + MurmurHash3_x64_128(buf, (int)n, h, h); uint64_t r; memcpy(&r, h, 8); return r; diff --git a/src/util/io/input_file.h b/src/util/io/input_file.h index cdb7fea29..4fdd480da 100644 --- a/src/util/io/input_file.h +++ b/src/util/io/input_file.h @@ -28,10 +28,6 @@ along with this program. If not, see . #include "deserializer.h" #include "temp_file.h" -using std::vector; -using std::string; -using std::runtime_error; - const size_t MEGABYTES = 1 << 20; const size_t GIGABYTES = 1 << 30; const size_t KILOBYTES = 1 << 10; @@ -41,12 +37,13 @@ struct InputFile : public Deserializer enum { BUFFERED = 1, NO_AUTODETECT = 2 }; - InputFile(const string &file_name, int flags = 0); + InputFile(const std::string &file_name, int flags = 0); InputFile(TempFile &tmp_file, int flags = 0); + InputFile(OutputFile& tmp_file, int flags = 0); void close_and_delete(); uint64_t hash(); - string file_name; - bool unlinked; + std::string file_name; + bool unlinked, temp_file; }; diff --git a/src/util/io/input_stream_buffer.cpp b/src/util/io/input_stream_buffer.cpp index 6b6b06cdf..6ed248a00 100644 --- a/src/util/io/input_stream_buffer.cpp +++ b/src/util/io/input_stream_buffer.cpp @@ -44,11 +44,12 @@ void InputStreamBuffer::rewind() { prev_->rewind(); file_offset_ = 0; + putback_count_ = 0; } -void InputStreamBuffer::seek(size_t pos) +void InputStreamBuffer::seek(int64_t pos, int origin) { - prev_->seek(pos); + prev_->seek(pos, origin); file_offset_ = 0; } @@ -102,7 +103,7 @@ void InputStreamBuffer::close() { prev_->close(); } -size_t InputStreamBuffer::tell() { +int64_t InputStreamBuffer::tell() { if (!seekable()) throw std::runtime_error("Calling tell on non seekable stream."); return file_offset_; diff --git a/src/util/io/input_stream_buffer.h b/src/util/io/input_stream_buffer.h index 94977e3dc..9def50de7 100644 --- a/src/util/io/input_stream_buffer.h +++ b/src/util/io/input_stream_buffer.h @@ -30,12 +30,12 @@ struct InputStreamBuffer : public StreamEntity enum { ASYNC = 4 }; InputStreamBuffer(StreamEntity* prev, int flags = 0); virtual void rewind() override; - virtual void seek(size_t pos) override; + virtual void seek(int64_t p, int origin) override; virtual void seek_forward(size_t n) override; - virtual pair read() override; + virtual std::pair read() override; virtual void putback(const char* p, size_t n) override; virtual void close() override; - virtual size_t tell() override; + virtual int64_t tell() override; private: static void load_worker(InputStreamBuffer *buf); diff --git a/src/util/io/output_file.cpp b/src/util/io/output_file.cpp index 077ce7f2b..43fd36217 100644 --- a/src/util/io/output_file.cpp +++ b/src/util/io/output_file.cpp @@ -29,6 +29,8 @@ along with this program. If not, see . #include "zstd_stream.h" #endif +using std::string; + static StreamEntity* make_compressor(const Compressor c, StreamEntity* buffer) { switch (c) { case Compressor::ZLIB: @@ -55,7 +57,7 @@ OutputFile::OutputFile(const string &file_name, Compressor compressor, const cha } #ifndef _MSC_VER -OutputFile::OutputFile(pair fd, const char *mode): +OutputFile::OutputFile(std::pair fd, const char *mode): Serializer(new OutputStreamBuffer(new FileSink(fd.first, fd.second, mode))), file_name_(fd.first) { diff --git a/src/util/io/output_file.h b/src/util/io/output_file.h index 32493fcd2..82246cba4 100644 --- a/src/util/io/output_file.h +++ b/src/util/io/output_file.h @@ -24,16 +24,13 @@ along with this program. If not, see . #include "serializer.h" #include "../text_buffer.h" -using std::string; -using std::pair; - enum class Compressor { NONE, ZLIB, ZSTD }; struct OutputFile : public Serializer { - OutputFile(const string &file_name, Compressor compressor = Compressor::NONE, const char *mode = "wb"); + OutputFile(const std::string &file_name, Compressor compressor = Compressor::NONE, const char *mode = "wb"); #ifndef _MSC_VER - OutputFile(pair fd, const char *mode); + OutputFile(std::pair fd, const char *mode); #endif void remove(); @@ -49,13 +46,13 @@ struct OutputFile : public Serializer } } - string file_name() const + std::string file_name() const { return file_name_; } protected: - string file_name_; + std::string file_name_; }; diff --git a/src/util/io/output_stream_buffer.cpp b/src/util/io/output_stream_buffer.cpp index d46ba802b..a28f42a45 100644 --- a/src/util/io/output_stream_buffer.cpp +++ b/src/util/io/output_stream_buffer.cpp @@ -19,6 +19,8 @@ along with this program. If not, see . #include "../../basic/config.h" #include "output_stream_buffer.h" +using std::pair; + OutputStreamBuffer::OutputStreamBuffer(StreamEntity* prev): StreamEntity(prev), buf_size_(prev->file_name().empty() ? STDOUT_BUF_SIZE : config.file_buffer_size), @@ -35,9 +37,9 @@ void OutputStreamBuffer::flush(size_t count) prev_->write(buf_.get(), count); } -void OutputStreamBuffer::seek(size_t pos) +void OutputStreamBuffer::seek(int64_t p, int origin) { - prev_->seek(pos); + prev_->seek(p, origin); } void OutputStreamBuffer::rewind() @@ -45,7 +47,7 @@ void OutputStreamBuffer::rewind() prev_->rewind(); } -size_t OutputStreamBuffer::tell() +int64_t OutputStreamBuffer::tell() { return prev_->tell(); } diff --git a/src/util/io/output_stream_buffer.h b/src/util/io/output_stream_buffer.h index 2f3623ee8..182daeb66 100644 --- a/src/util/io/output_stream_buffer.h +++ b/src/util/io/output_stream_buffer.h @@ -16,9 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef OUTPUT_STREAM_BUFFER_H_ -#define OUTPUT_STREAM_BUFFER_H_ - +#pragma once #include #include #include "stream_entity.h" @@ -26,16 +24,14 @@ along with this program. If not, see . struct OutputStreamBuffer : public StreamEntity { OutputStreamBuffer(StreamEntity* prev); - virtual pair write_buffer(); - virtual void flush(size_t count); - virtual void seek(size_t pos); - virtual void rewind(); - virtual size_t tell(); + virtual std::pair write_buffer() override; + virtual void flush(size_t count) override; + virtual void seek(int64_t p, int origin) override; + virtual void rewind() override; + virtual int64_t tell() override; private: static const size_t STDOUT_BUF_SIZE = 4096; const size_t buf_size_; std::unique_ptr buf_; -}; - -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/src/util/io/record_reader.h b/src/util/io/record_reader.h index 6e1184416..41613c4dd 100644 --- a/src/util/io/record_reader.h +++ b/src/util/io/record_reader.h @@ -3,6 +3,7 @@ #include #include +#include <../basic/value.h> #include "deserializer.h" struct Finish {}; @@ -51,15 +52,27 @@ struct DynamicRecordReader { return *this; } - void operator>>(const Finish&) { - if (size_ == 0) - return; - char *buf = new char[size_]; - d_.read(buf, size_); - delete[] buf; - size_ = 0; + DynamicRecordReader& operator>>(int& x) + { + if (size_ >= sizeof(int)) { + d_ >> x; + size_ -= sizeof(int); + } + else + x = 0; + + return *this; } + void operator>>(const Finish&) { + if (size_ == 0) + return; + char *buf = new char[size_]; + d_.read(buf, size_); + delete[] buf; + size_ = 0; + } + private: Deserializer &d_; uint64_t size_; diff --git a/src/util/io/serializer.cpp b/src/util/io/serializer.cpp index 92ed124e7..c9d15c2c9 100644 --- a/src/util/io/serializer.cpp +++ b/src/util/io/serializer.cpp @@ -20,6 +20,9 @@ along with this program. If not, see . #include #include "serializer.h" +using std::string; +using std::pair; + Serializer::Serializer(StreamEntity *buffer, int flags) : buffer_(buffer), varint_(flags & VARINT) @@ -38,10 +41,10 @@ void Serializer::close() buffer_->close(); } -void Serializer::seek(size_t pos) +void Serializer::seek(int64_t p, int origin) { flush(); - buffer_->seek(pos); + buffer_->seek(p, origin); reset_buffer(); } diff --git a/src/util/io/serializer.h b/src/util/io/serializer.h index 7e9bdc9f1..0a1384fdc 100644 --- a/src/util/io/serializer.h +++ b/src/util/io/serializer.h @@ -16,9 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef SERIALIZER_H_ -#define SERIALIZER_H_ - +#pragma once #include #include #include @@ -34,9 +32,24 @@ struct Serializer : public Consumer Serializer(StreamEntity *buffer, int flags = 0); - Serializer& operator<<(int x) + Serializer& operator<<(int32_t x) { - write(x); + if (varint_) + write_varint((uint32_t)x, *this); + else + write(big_endian_byteswap(x)); + return *this; + } + + Serializer& operator<<(long long x) + { + write(big_endian_byteswap(x)); + return *this; + } + + Serializer& operator<<(long x) + { + write(big_endian_byteswap(x)); return *this; } @@ -74,6 +87,14 @@ struct Serializer : public Consumer return *this; } + Serializer& operator<<(const std::vector &v) + { + *this << (unsigned)v.size(); + for (std::vector::const_iterator i = v.begin(); i < v.end(); ++i) + *this << *i; + return *this; + } + Serializer& operator<<(const std::set &v) { *this << (unsigned)v.size(); @@ -82,6 +103,14 @@ struct Serializer : public Consumer return *this; } + Serializer& operator<<(const std::set& v) + { + *this << (unsigned)v.size(); + for (std::set::const_iterator i = v.begin(); i != v.end(); ++i) + *this << *i; + return *this; + } + Serializer& operator<<(const std::string &s) { write(s.c_str(), s.length() + 1); @@ -130,7 +159,7 @@ struct Serializer : public Consumer void set(int flag); void unset(int flag); void write_raw(const char *ptr, size_t count); - void seek(size_t pos); + void seek(int64_t p, int origin = SEEK_SET); void rewind(); size_t tell(); void close(); @@ -154,6 +183,4 @@ struct Serializer : public Consumer char *begin_, *next_, *end_; bool varint_; -}; - -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/src/util/io/stream_entity.h b/src/util/io/stream_entity.h index c8f609138..e80817b6e 100644 --- a/src/util/io/stream_entity.h +++ b/src/util/io/stream_entity.h @@ -16,16 +16,11 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef STREAM_ENTITY_H_ -#define STREAM_ENTITY_H_ - +#pragma once #include #include #include "exceptions.h" -using std::string; -using std::pair; - struct StreamEntity { StreamEntity(bool seekable = false): @@ -40,7 +35,7 @@ struct StreamEntity { throw UnsupportedOperation(); } - virtual void seek(size_t pos) + virtual void seek(int64_t p, int origin) { throw UnsupportedOperation(); } @@ -48,7 +43,7 @@ struct StreamEntity { throw UnsupportedOperation(); } - virtual size_t tell() + virtual int64_t tell() { throw UnsupportedOperation(); } @@ -56,7 +51,7 @@ struct StreamEntity { throw UnsupportedOperation(); } - virtual pair read() + virtual std::pair read() { throw UnsupportedOperation(); } @@ -64,7 +59,7 @@ struct StreamEntity { prev_->close(); } - virtual const string& file_name() const + virtual const std::string& file_name() const { return prev_->file_name(); } @@ -72,7 +67,7 @@ struct StreamEntity { throw UnsupportedOperation(); } - virtual pair write_buffer() + virtual std::pair write_buffer() { throw UnsupportedOperation(); } @@ -103,5 +98,3 @@ struct StreamEntity StreamEntity *prev_; bool seekable_; }; - -#endif \ No newline at end of file diff --git a/src/util/io/temp_file.cpp b/src/util/io/temp_file.cpp index a3cc108fc..27d24845a 100644 --- a/src/util/io/temp_file.cpp +++ b/src/util/io/temp_file.cpp @@ -36,6 +36,7 @@ along with this program. If not, see . using std::vector; using std::string; +using std::pair; unsigned TempFile::n = 0; uint64_t TempFile::hash_key; diff --git a/src/util/io/temp_file.h b/src/util/io/temp_file.h index 7309c9b76..4202c8b02 100644 --- a/src/util/io/temp_file.h +++ b/src/util/io/temp_file.h @@ -16,9 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef TEMP_FILE_H_ -#define TEMP_FILE_H_ - +#pragma once #include #include "output_file.h" @@ -41,6 +39,4 @@ struct TempFile : public OutputFile std::pair init(bool unlink); #endif -}; - -#endif /* TEMP_FILE_H_ */ +}; \ No newline at end of file diff --git a/src/util/io/text_input_file.cpp b/src/util/io/text_input_file.cpp index 1dd8270e5..986dc0909 100644 --- a/src/util/io/text_input_file.cpp +++ b/src/util/io/text_input_file.cpp @@ -18,6 +18,8 @@ along with this program. If not, see . #include "text_input_file.h" +using std::string; + TextInputFile::TextInputFile(const string &file_name) : InputFile(file_name), line_count(0), @@ -37,6 +39,15 @@ TextInputFile::TextInputFile(TempFile &tmp_file) : eof_(false) {} +TextInputFile::TextInputFile(OutputFile& out_file) : + InputFile(out_file), + line_count(0), + line_buf_used_(0), + line_buf_end_(0), + putback_line_(false), + eof_(false) +{} + void TextInputFile::rewind() { InputFile::rewind(); diff --git a/src/util/io/text_input_file.h b/src/util/io/text_input_file.h index bb02b7ce7..96caf5265 100644 --- a/src/util/io/text_input_file.h +++ b/src/util/io/text_input_file.h @@ -16,22 +16,17 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef TEXT_INPUT_FILE_H_ -#define TEXT_INPUT_FILE_H_ - +#pragma once #include #include #include #include "input_file.h" -using std::vector; -using std::string; -using std::runtime_error; - struct TextInputFile : public InputFile { - TextInputFile(const string &file_name); + TextInputFile(const std::string &file_name); TextInputFile(TempFile &tmp_file); + TextInputFile(OutputFile& out_file); void rewind(); bool eof() const; void putback(char c); @@ -41,7 +36,7 @@ struct TextInputFile : public InputFile return !eof(); } - string line; + std::string line; size_t line_count; protected: @@ -53,5 +48,3 @@ struct TextInputFile : public InputFile bool putback_line_, eof_; }; - -#endif diff --git a/src/util/io/zstd_stream.cpp b/src/util/io/zstd_stream.cpp index 441ad26ea..8b5f70f04 100644 --- a/src/util/io/zstd_stream.cpp +++ b/src/util/io/zstd_stream.cpp @@ -1,5 +1,7 @@ #include "zstd_stream.h" +using std::pair; + ZstdSink::ZstdSink(StreamEntity* prev) : StreamEntity(prev), stream(ZSTD_createCStream()) @@ -28,6 +30,8 @@ void ZstdSink::write(const char* ptr, size_t count) void ZstdSink::close() { + if (!stream) + return; ZSTD_outBuffer out_buf; size_t n; do { @@ -40,6 +44,7 @@ void ZstdSink::close() prev_->flush(out_buf.pos); } while (n > 0); ZSTD_freeCStream(stream); + stream = nullptr; prev_->close(); } @@ -84,7 +89,10 @@ size_t ZstdSource::read(char* ptr, size_t count) void ZstdSource::close() { + if (!stream) + return; ZSTD_freeDStream(stream); + stream = nullptr; prev_->close(); } diff --git a/src/util/kmer/filter.cpp b/src/util/kmer/filter.cpp new file mode 100644 index 000000000..5c9006242 --- /dev/null +++ b/src/util/kmer/filter.cpp @@ -0,0 +1,79 @@ +#include "filter.h" +#include "kmer.h" + +using std::pair; +using std::runtime_error; +using std::move; + +template +static KmerFilter build(Sequence seq) { + KmerIterator it(seq); + BitVector v(power(20, K)); + Loc n = 0; + while (it.good()) { + assert(*it < v.size()); + v.set(*it); + ++it; + ++n; + } + return KmerFilter{ K, n, move(v) }; +} + +static KmerFilter build(Sequence seq, int k) { + switch (k) { + case 2: + return build<2>(seq); + case 3: + return build<3>(seq); + case 4: + return build<4>(seq); + case 5: + return build<5>(seq); + default: + throw runtime_error("Unsupported kmer size"); + } +} + +KmerFilter::KmerFilter(int k, Loc count, BitVector&& table): + k_(k), + count_(count), + table_(move(table)) +{} + +KmerFilter::KmerFilter(Sequence seq, int k) : + KmerFilter(build(seq, k)) +{ +} + +template +static pair covered(const BitVector& v, Sequence seq) { + KmerIterator it(seq); + Loc n = 0, s = 0; + while (it.good()) { + if (v.get(*it)) + ++s; + ++it; + ++n; + } + return { n,s }; +} + +static pair covered(const BitVector& v, Sequence seq, int k) { + switch (k) { + case 2: + return covered<2>(v, seq); + case 3: + return covered<3>(v, seq); + case 4: + return covered<4>(v, seq); + case 5: + return covered<5>(v, seq); + default: + throw runtime_error("Unsupported kmer size"); + } +} + +pair KmerFilter::covered(Sequence seq) const { + const pair n = ::covered(table_, seq, k_); + return { (double)n.second / count_, (double)n.second / n.first }; +} \ No newline at end of file diff --git a/src/util/kmer/filter.h b/src/util/kmer/filter.h new file mode 100644 index 000000000..77708a6c5 --- /dev/null +++ b/src/util/kmer/filter.h @@ -0,0 +1,19 @@ +#include "../../basic/sequence.h" +#include "../data_structures/bit_vector.h" + +struct KmerFilter { + + KmerFilter(Sequence seq, int k); + std::pair covered(Sequence seq) const; + +private: + + KmerFilter(int k, Loc count, BitVector&& table); + + const int k_; + const Loc count_; + const BitVector table_; + + template friend KmerFilter build(Sequence); + +}; \ No newline at end of file diff --git a/src/util/kmer/kmer.h b/src/util/kmer/kmer.h new file mode 100644 index 000000000..cb4efb2fd --- /dev/null +++ b/src/util/kmer/kmer.h @@ -0,0 +1,108 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2022 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once +#include +#include "../../basic/value.h" +#include "../../basic/sequence.h" +#include "../math/integer.h" + +struct IdentityReduction { + int bit_size() const { + return 5; + } + uint64_t size() const { + return 20; + } + uint64_t operator()(uint64_t x) const { + return x; + } +}; + +template +struct Kmer { + Kmer() : + code(0) + { } + Kmer(const char* s) : + code(0) + { + assert(strlen(s) == K); + for (size_t i = 0; i < K; ++i) + code = (code * TRUE_AA) + (uint64_t)amino_acid_traits.from_char(*s++); + } + operator uint64_t() const { + return code; + } + struct Hash { + size_t operator()(const Kmer k) const { + return std::hash()(k.code); + } + }; + uint64_t code; +}; + +template +struct KmerIterator { + KmerIterator(const Sequence& seq, const R& reduction = R()) : + reduction_(reduction), + ptr_(seq.data() - 1), + end_(seq.end()), + mod_(power((uint64_t)reduction_.size(), K - 1)) + { + inc(0, 1); + } + Kmer operator*() const { + return kmer_; + } + bool good() const { + return ptr_ < end_; + } + KmerIterator& operator++() { + inc(K - 1, mod_); + return *this; + } + Loc operator-(const Letter* ptr) { + return Loc(ptr_ + 1 - K - ptr); + } +private: + void inc(uint64_t n, uint64_t mod) { + kmer_.code %= mod; + do { + ++ptr_; + if (ptr_ == end_) + return; + const uint64_t l = letter_mask(*ptr_); + if (l < TRUE_AA) { + kmer_.code = (kmer_.code * reduction_.size()) + reduction_(l); + ++n; + } + else { + kmer_.code = 0; + n = 0; + } + } while (n < K); + //kmer_.code &= (uint64_t(1) << (reduction_.size() * K)) - 1; + } + const R reduction_; + const Letter* ptr_, *const end_; + const uint64_t mod_; + Kmer kmer_; +}; diff --git a/src/util/log_stream.h b/src/util/log_stream.h index 94985c434..f0aa776f9 100644 --- a/src/util/log_stream.h +++ b/src/util/log_stream.h @@ -28,11 +28,11 @@ along with this program. If not, see . #include #include -struct Message_stream +struct MessageStream { - Message_stream(bool to_cout = true, bool to_file = false); - template - Message_stream& operator<<(const _t& x) + MessageStream(bool to_cout = true, bool to_file = false); + template + MessageStream& operator<<(const T& x) { if(to_cout_) (*out_stream_) << x; @@ -44,34 +44,43 @@ struct Message_stream return *this; } //Message_stream& operator<<(std::ostream & (__cdecl *_Pfn)(std::ostream&)) - Message_stream& operator<<(std::ostream& (*_Pfn)(std::ostream&)); + MessageStream& operator<<(std::ostream& (*_Pfn)(std::ostream&)); static std::mutex mtx; private: std::ostream* out_stream_; bool to_cout_, to_file_; }; -extern Message_stream message_stream; -extern Message_stream verbose_stream; -extern Message_stream log_stream; +extern MessageStream message_stream; +extern MessageStream verbose_stream; +extern MessageStream log_stream; struct task_timer { - task_timer(unsigned level = 1) : + task_timer(MessageStream& stream, unsigned level = 1) : level_(level), - msg_(nullptr) + msg_(nullptr), + stream_(stream) { start(nullptr); } - task_timer(const char *msg, unsigned level=1) : + task_timer(unsigned level = 1) : + task_timer(get_stream(), level) + {} + task_timer(const char* msg, MessageStream& stream, unsigned level = 1) : level_(level), - msg_(msg) + msg_(msg), + stream_(stream) { start(msg); } + task_timer(const char* msg, unsigned level = 1): + task_timer(msg, get_stream(), level) + {} ~task_timer() { - finish(); + if (!std::uncaught_exception()) + finish(); } void go(const char* msg = nullptr) { @@ -79,11 +88,14 @@ struct task_timer start(msg); msg_ = msg; } + void go(const std::string& s) { + go(s.c_str()); + } void finish() { if (!msg_ || level_ == UINT_MAX) return; - get_stream() << " [" << get() << "s]" << std::endl; + stream_ << " [" << get() << "s]" << std::endl; msg_ = 0; } double get() @@ -102,7 +114,18 @@ struct task_timer uint64_t nanoseconds() const { return (uint64_t)std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - t).count(); } - Message_stream& get_stream() const +private: + void start(const char *msg) + { + t = std::chrono::high_resolution_clock::now(); + if (level_ == UINT_MAX) + return; + if (!msg) + return; + stream_ << msg << "... " << std::flush; + + } + MessageStream& get_stream() const { switch (level_) { case 1: @@ -115,19 +138,9 @@ struct task_timer return message_stream; } } -private: - void start(const char *msg) - { - t = std::chrono::high_resolution_clock::now(); - if (level_ == UINT_MAX) - return; - if (!msg) - return; - get_stream() << msg << "... " << std::flush; - - } unsigned level_; const char *msg_; + MessageStream& stream_; std::chrono::high_resolution_clock::time_point t; }; diff --git a/src/util/math/integer.h b/src/util/math/integer.h index e990963fe..8b25f57ec 100644 --- a/src/util/math/integer.h +++ b/src/util/math/integer.h @@ -37,7 +37,7 @@ static inline int32_t saturated_add(int32_t x, int32_t y) { } static inline size_t bit_length(size_t x) { - return ceil(log(x) / log(2)) + 1; + return (size_t)ceil(log(x) / log(2)) + 1; } static inline uint64_t next_power_of_2(double x) @@ -59,4 +59,17 @@ bool next_combination(_it begin, _it end) { } } return false; +} + +template +I power(I x, I p) +{ + if (p == 0) return 1; + if (p == 1) return x; + + const I t = power(x, p / 2); + if (p % 2 == 0) + return t * t; + else + return x * t * t; } \ No newline at end of file diff --git a/src/util/options/option.h b/src/util/options/option.h index c15ab7466..b73c4703b 100644 --- a/src/util/options/option.h +++ b/src/util/options/option.h @@ -1,5 +1,29 @@ +#include +#include #pragma once +struct OptionsGroup; + +struct OptionBase +{ + OptionBase(const std::string& id, char short_id, const std::string& desc, bool disabled, const OptionsGroup* group) : + id(id), + desc(desc), + short_id(short_id), + disabled(disabled), + group(group) + {} + virtual void read(const std::vector& v) = 0; + virtual bool present() = 0; + virtual void set_default() = 0; + virtual ~OptionBase() + {} + const std::string id, desc; + const char short_id; + bool disabled; + const OptionsGroup* group; +}; + template struct Option : public T { Option(): @@ -8,11 +32,102 @@ struct Option : public T { bool present() const { return present_; } + bool blank() const { + return !present_; + } Option& operator=(const T& value) { *static_cast(this) = value; present_ = true; return *this; } + void require() { + if(!present_) + throw std::runtime_error("Missing parameter: --" + base_->id + "/-" + base_->short_id); + } + T get(const T& default_value) const { + return present_ ? *this : default_value; + } + void unset() { + present_ = false; + } +private: + bool present_; + const OptionBase* base_; + template friend void set_base_ptr(Option&, const OptionBase*); +}; + +template<> +struct Option { + Option() : + present_(false) + {} + bool present() const { + return present_; + } + bool blank() const { + return !present_; + } + Option& operator=(const double value) { + value_ = value; + present_ = true; + return *this; + } + void set_if_blank(const double value) { + if (!present_) + this->operator=(value); + } + void unset() { + present_ = false; + } + operator double() const { + if (!present_) + throw std::runtime_error("Option::present"); + return value_; + } + double get(const double default_value) const { + return present_ ? value_ : default_value; + } +private: + double value_; + bool present_; + const OptionBase* base_; + template friend void set_base_ptr(Option&, const OptionBase*); +}; + +template<> +struct Option { + Option() : + present_(false) + {} + bool present() const { + return present_; + } + bool blank() const { + return !present_; + } + Option& operator=(const int64_t value) { + value_ = value; + present_ = true; + return *this; + } + void set_if_blank(const int64_t value) { + if (!present_) + this->operator=(value); + } + void unset() { + present_ = false; + } + operator int64_t() const { + if (!present_) + throw std::runtime_error("Option::present"); + return value_; + } + int64_t get(const int64_t default_value) const { + return present_ ? value_ : default_value; + } private: + int64_t value_; bool present_; + const OptionBase* base_; + template friend void set_base_ptr(Option&, const OptionBase*); }; \ No newline at end of file diff --git a/src/util/parallel/filestack.cpp b/src/util/parallel/filestack.cpp index df0095b50..c40ab4337 100644 --- a/src/util/parallel/filestack.cpp +++ b/src/util/parallel/filestack.cpp @@ -253,7 +253,7 @@ int FileStack::remove(const string & line) { return 0; } -int FileStack::push_non_locked(const string & buf) { +int64_t FileStack::push_non_locked(const string & buf) { DBG(""); static const string nl("\n"); #ifndef WIN32 @@ -268,14 +268,14 @@ int FileStack::push_non_locked(const string & buf) { #endif } -int FileStack::push(const string & buf, size_t & size_after_push) { +int64_t FileStack::push(const string & buf, size_t & size_after_push) { DBG(""); bool locked_internally = false; if (! locked) { lock(); locked_internally = true; } - size_t n = push_non_locked(buf); + int64_t n = push_non_locked(buf); if (size_after_push != numeric_limits::max()) { size_after_push = size(); } @@ -285,13 +285,13 @@ int FileStack::push(const string & buf, size_t & size_after_push) { return n; } -int FileStack::push(const string & buf) { +int64_t FileStack::push(const string & buf) { DBG(""); size_t size_after_push = numeric_limits::max(); return push(buf, size_after_push); } -int FileStack::push(int i) { +int64_t FileStack::push(int i) { DBG(""); string buf = to_string(i); return push(buf); @@ -301,7 +301,7 @@ int FileStack::push(int i) { size_t FileStack::size() { DBG(""); - size_t n_bytes, i, c = 0; + size_t c = 0; const size_t chunk_size = default_max_line_length; char * raw = new char[chunk_size * sizeof(char)]; @@ -312,6 +312,7 @@ size_t FileStack::size() { } #ifndef WIN32 + size_t n_bytes, i; lseek(fd, 0, SEEK_SET); while ((n_bytes = read(fd, raw, chunk_size)) > 0) { for (i=0; i #include #include +#include "../system.h" #ifndef WIN32 #include #include @@ -221,7 +222,7 @@ void Parallelizer::sleep(const double sleep_s) { bool Parallelizer::clean(vector & file_list) { for (auto s : file_list) { errno = 0; - unlink(s.c_str()); + UNLINK(s.c_str()); } file_list.clear(); return true; diff --git a/src/util/parallel/thread_pool.h b/src/util/parallel/thread_pool.h index c61162664..59829d465 100644 --- a/src/util/parallel/thread_pool.h +++ b/src/util/parallel/thread_pool.h @@ -1,22 +1,46 @@ -#ifndef THREAD_POOL_H_ -#define THREAD_POOL_H_ +/**** +DIAMOND protein aligner +Copyright (C) 2021-2022 Max Planck Society for the Advancement of Science e.V. +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once +#include #include #include #include +#include +#include +#include +#include +#include "../log_stream.h" namespace Util { namespace Parallel { -template -void pool_worker(std::atomic *partition, size_t thread_id, size_t partition_count, _f f, _args... args) { +template +void pool_worker(std::atomic *partition, size_t thread_id, size_t partition_count, F f, Args... args) { size_t p; while ((p = (*partition)++) < partition_count) { f(p, thread_id, args...); } } -template -void scheduled_thread_pool(size_t thread_count, _f f, _args... args) { +template +void scheduled_thread_pool(size_t thread_count, F f, Args... args) { std::atomic partition(0); std::vector threads; for (size_t i = 0; i < thread_count; ++i) @@ -25,11 +49,218 @@ void scheduled_thread_pool(size_t thread_count, _f f, _args... args) { t.join(); } -template -void scheduled_thread_pool_auto(size_t thread_count, size_t partition_count, _f f, _args... args) { - scheduled_thread_pool(thread_count, pool_worker<_f, _args...>, partition_count, f, args...); +template +void scheduled_thread_pool_auto(size_t thread_count, size_t partition_count, F f, Args... args) { + scheduled_thread_pool(thread_count, pool_worker, partition_count, f, args...); +} + +template +void launch_threads(int thread_count, F& f) { + std::vector threads; + for (int i = 0; i < thread_count; ++i) + threads.emplace_back(f); + for (std::thread& t : threads) + t.join(); } }} -#endif \ No newline at end of file +struct ThreadPool { + + enum { PRIORITY_COUNT = 2 }; + + struct TaskSet { + TaskSet(ThreadPool& thread_pool, int priority): + priority(priority), + total_(0), + finished_(0), + thread_pool(&thread_pool) + {} + void finish() { + ++finished_; + if (finished()) { + cv_.notify_all(); + thread_pool->cv_.notify_all(); + } + } + bool finished() const { + return total_ == finished_; + } + int64_t total() const { + return total_; + } + void add() { + ++total_; + } + void wait() { + std::unique_lock lock(mtx_); + cv_.wait(lock, [this] {return this->finished(); }); + } + void run() { + if (finished()) + return; + thread_pool->run_set(this); + } + template + void enqueue(F&& f, Args&&... args) { + thread_pool->enqueue(*this, std::forward(f), std::forward(args)...); + } + const int priority; + private: + std::atomic total_, finished_; + ThreadPool* thread_pool; + std::mutex mtx_; + std::condition_variable cv_; + friend struct ThreadPool; + }; + + struct Task { + Task(): + task_set(nullptr) + {} + Task(std::function f): + f(f), + task_set(nullptr) + {} + Task(std::function f, TaskSet& task_set): + f(f), + task_set(&task_set) + {} + operator bool() const { + return f.operator bool(); + } + std::function f; + TaskSet* task_set; + }; + + template + void enqueue(TaskSet& task_set, F&& f, Args&&... args) + { + auto task = std::bind(std::forward(f), std::forward(args)...); + { + std::unique_lock lock(mtx_); + + /*if (stop_) + throw std::runtime_error("enqueue on stopped ThreadPool");*/ + + task_set.add(); + tasks_[task_set.priority].emplace([task]() { task(); }, task_set); + } + cv_.notify_one(); + } + + void run_set(TaskSet* task_set) { + for (;;) + { + Task task; + + if (!task_set && run_default_) { + if (!queue_empty()) { + std::unique_lock lock(this->mtx_); + task = pop_task(); + } + if (!task) { + ++default_started_; + if (!default_task_(*this)) + run_default_ = false; + ++default_finished_; + if (!run_default_ && default_started_ == default_finished_) { + stop_ = true; + cv_.notify_all(); + } + continue; + } + } + else { + std::unique_lock lock(this->mtx_); + this->cv_.wait(lock, + [this, task_set] { return (stop_ && !task_set) || !queue_empty(task_set ? task_set->priority : PRIORITY_COUNT-1) || (task_set && task_set->finished()); }); + if ((stop_ && queue_empty() && !task_set) || (task_set && task_set->finished())) { + if (!task_set) + ++threads_finished_; + return; + } + task = pop_task(task_set ? task_set->priority : PRIORITY_COUNT - 1); + } + + task.f(); + if (task.task_set) + task.task_set->finish(); + } + } + + ThreadPool(const std::function& default_task = std::function()) : + default_task_(default_task), + mtx_(), + stop_(false), + run_default_(default_task.operator bool()), + default_started_(0), + default_finished_(0), + threads_finished_(0) + { + } + + void run(size_t threads, bool heartbeat = false) { + for (size_t i = 0; i < threads; ++i) + workers_.emplace_back([this] { this->run_set(nullptr); }); + if (heartbeat) + heartbeat_ = std::thread([&]() { + while (!stop_) { + log_stream << "Workers=" << workers_.size() << '/' << threads_finished_ << " started = " << default_started_ << " finished = " + << default_finished_ << " queue=" << queue_len(0) << '/' << queue_len(1) << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(1)); + }}); + } + + ~ThreadPool() + { + { + std::unique_lock lock(mtx_); + stop_ = true; + } + cv_.notify_all(); + join(); + } + + void join() { + for (std::thread &worker : workers_) + worker.join(); + workers_.clear(); + if(heartbeat_.joinable()) + heartbeat_.join(); + } + + int64_t queue_len(int priority) const { + return tasks_[priority].size(); + } + +private: + + bool queue_empty(int priority = PRIORITY_COUNT - 1) { + for (int i = 0; i <= priority; ++i) + if (!tasks_[i].empty()) + return false; + return true; + } + + Task pop_task(int priority = PRIORITY_COUNT - 1) { + Task task; + for (int i = 0; i <= priority; ++i) + if (!tasks_[i].empty()) { + task = std::move(tasks_[i].front()); + tasks_[i].pop(); + break; + } + return task; + } + + std::array, PRIORITY_COUNT> tasks_; + std::function default_task_; + std::vector workers_; + std::thread heartbeat_; + std::mutex mtx_; + std::condition_variable cv_; + bool stop_, run_default_; + std::atomic default_started_, default_finished_, threads_finished_; + +}; diff --git a/src/util/queue.h b/src/util/queue.h deleted file mode 100644 index 96410df89..000000000 --- a/src/util/queue.h +++ /dev/null @@ -1,62 +0,0 @@ -/**** -DIAMOND protein aligner -Copyright (C) 2013-2017 Benjamin Buchfink - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -****/ - -#include -#include -#include - -struct Queue -{ - enum { end = size_t(-1) }; - Queue(size_t begin, size_t end) : - next_(begin), - block_(false), - end_(end) - {} - template - size_t get(_f &f) - { - std::unique_lock lock(mtx_); - while (block_) - cond_.wait(lock); - const size_t q = next_++; - if (q >= end_) { - return Queue::end; - } - block_ = f(q); - return q; - } - size_t next() const - { - return next_; - } - size_t get_end() const - { - return end_; - } - void release() { - block_ = false; - cond_.notify_all(); - } -private: - std::mutex mtx_; - std::condition_variable cond_; - volatile size_t next_; - volatile bool block_; - const size_t end_; -}; \ No newline at end of file diff --git a/src/util/range.h b/src/util/range.h index 992aeae29..de188d32b 100644 --- a/src/util/range.h +++ b/src/util/range.h @@ -1,21 +1,20 @@ -#ifndef RANGE_H_ -#define RANGE_H_ +#pragma once -template +template struct Range { Range() {} - Range(const _it &begin, const _it &end): + Range(const It& begin, const It& end): begin_(begin), end_(end) {} - _it begin() const { + It begin() const { return begin_; } - _it end() const { + It end() const { return end_; } @@ -25,8 +24,6 @@ struct Range { private: - _it begin_, end_; + It begin_, end_; -}; - -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/src/util/seq_file_format.cpp b/src/util/seq_file_format.cpp index f7550bbd7..db3afb70b 100644 --- a/src/util/seq_file_format.cpp +++ b/src/util/seq_file_format.cpp @@ -20,15 +20,17 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ +#include #include "seq_file_format.h" +using std::unique_ptr; using std::string; using std::vector; struct Raw_text {}; struct Sequence_data {}; -template +template inline char convert_char(char a, const ValueTraits& value_traits) { return a; @@ -40,11 +42,11 @@ inline char convert_char(char a, const ValueTraits& value_traits) return value_traits.from_char(a); } -template -void copy_line(const string & s, vector<_t>& v, size_t d, const ValueTraits& value_traits, _what) +template +void copy_line(const string & s, vector& v, size_t d, const ValueTraits& value_traits, What) { for (string::const_iterator i = s.begin() + d; i != s.end(); ++i) - v.push_back(convert_char<_what>(*i, value_traits)); + v.push_back(convert_char(*i, value_traits)); } bool FASTA_format::get_seq(string& id, vector& seq, TextInputFile & s, const ValueTraits& value_traits, vector *qual) const @@ -106,18 +108,15 @@ bool FASTQ_format::get_seq(string& id, vector& seq, TextInputFile & s, c return true; } -const Sequence_file_format * guess_format(TextInputFile &file) +unique_ptr guess_format(TextInputFile &file) { - static const FASTA_format fasta; - static const FASTQ_format fastq; - file.getline(); file.putback_line(); if (file.line.empty()) throw std::runtime_error("Error detecting input file format. First line seems to be blank."); switch (file.line[0]) { - case '>': return &fasta; - case '@': return &fastq; + case '>': return unique_ptr { new FASTA_format() }; + case '@': return unique_ptr { new FASTQ_format() }; default: throw std::runtime_error("Error detecting input file format. First line must begin with '>' (FASTA) or '@' (FASTQ)."); } return 0; diff --git a/src/util/seq_file_format.h b/src/util/seq_file_format.h index f6421f135..fcedfc2c3 100644 --- a/src/util/seq_file_format.h +++ b/src/util/seq_file_format.h @@ -23,6 +23,7 @@ along with this program. If not, see . #pragma once #include #include +#include #include "../basic/value.h" #include "io/text_input_file.h" @@ -69,4 +70,4 @@ struct FASTQ_format : public Sequence_file_format }; -const Sequence_file_format* guess_format(TextInputFile &file); \ No newline at end of file +std::unique_ptr guess_format(TextInputFile &file); \ No newline at end of file diff --git a/src/util/sequence/sequence.cpp b/src/util/sequence/sequence.cpp index cec7600a4..126d29b8d 100644 --- a/src/util/sequence/sequence.cpp +++ b/src/util/sequence/sequence.cpp @@ -23,6 +23,13 @@ along with this program. If not, see . #include #include "sequence.h" #include "../util.h" +#include "../../stats/score_matrix.h" +#include "translate.h" + +using std::vector; +using std::array; +using std::string; +using std::min; namespace Util { namespace Seq { @@ -103,4 +110,62 @@ bool is_fully_masked(const Sequence& seq) { return n == seq.length(); } +array, 6> translate(const Sequence& seq) { + array, 6> out; + if (seq.length() < 3) + return out; + Translator::translate(seq, out.data()); + return out; +} + +Loc find_orfs(vector& seq, const Loc min_len) { + vector::iterator it, begin = seq.begin(); + Loc n = 0; + while ((it = std::find(begin, seq.end(), STOP_LETTER)) != seq.end()) { + const Loc l = Loc(it - begin); + if (l < min_len) + std::fill(begin, it, MASK_LETTER); + else + n += l; + begin = it + 1; + } + const Loc l = Loc(seq.end() - begin); + if (l < min_len) + std::fill(begin, seq.end(), MASK_LETTER); + else + n += l; + return n; +} + +bool looks_like_dna(const Sequence& seq) { + array count; + count.fill(0); + for (Loc i = 0; i < seq.length(); ++i) + ++count[(int)seq[i]]; + return count[(int)value_traits.from_char('A')] + + count[(int)value_traits.from_char('C')] + + count[(int)value_traits.from_char('G')] + + count[(int)value_traits.from_char('T')] + + count[(int)value_traits.from_char('N')] == seq.length(); +} + +std::vector window_scores(Sequence seq1, Sequence seq2, Loc window) { + assert(seq1.length() == seq2.length()); + vector v; + v.reserve(seq1.length()); + Score s = 0; + const Loc l = min(seq1.length(), window); + for (Loc i = 0; i < l; ++i) { + s += score_matrix(seq1[i], seq2[i]); + v.push_back(s); + } + Loc j = 0; + for (Loc i = window; i < seq1.length(); ++i, ++j) { + s += score_matrix(seq1[i], seq2[i]); + s -= score_matrix(seq1[j], seq2[j]); + v.push_back(s); + } + return v; +} + }} \ No newline at end of file diff --git a/src/util/sequence/sequence.h b/src/util/sequence/sequence.h index 1a72583ce..abcc56730 100644 --- a/src/util/sequence/sequence.h +++ b/src/util/sequence/sequence.h @@ -21,6 +21,8 @@ along with this program. If not, see . #pragma once #include +#include +#include #include #include "../../basic/sequence.h" #include "../io/output_file.h" @@ -47,5 +49,43 @@ std::string all_seqids(const char* s); std::string seqid(const char* title, bool short_seqids); void get_title_def(const std::string& s, std::string& title, std::string& def); bool is_fully_masked(const Sequence& seq); +std::array, 6> translate(const Sequence& seq); +Loc find_orfs(std::vector& seq, const Loc min_len); +bool looks_like_dna(const Sequence& seq); +std::vector window_scores(Sequence seq1, Sequence seq2, Loc window); + +struct FastaIterator { + FastaIterator(const char* ptr, const char* end) : + ptr_(ptr), + end_(end) + {} + bool good() const { + return ptr_ < end_; + } + std::string operator*() const { + if (*ptr_ == '>') { + return seqid(ptr_ + 1, false); + } + else { + std::string r; + const char* p = ptr_; + do { + const char* n = std::find(p, end_, '\n'); + r.append(p, n); + p = n + 1; + } while (p < end_); + return r; + } + } + FastaIterator& operator++() { + if (*ptr_ == '>') + ptr_ = std::find(ptr_, end_, '\n') + 1; + else + ptr_ = end_; + return *this; + } +private: + const char* ptr_, *end_; +}; }} diff --git a/src/basic/translate.h b/src/util/sequence/translate.h similarity index 80% rename from src/basic/translate.h rename to src/util/sequence/translate.h index f1d015a74..35ce3f10b 100644 --- a/src/basic/translate.h +++ b/src/util/sequence/translate.h @@ -16,13 +16,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ****/ -#ifndef TRANSLATE_H_ -#define TRANSLATE_H_ - +#pragma once #include -#include "value.h" - -using std::vector; +#include "../../basic/value.h" struct Translator { @@ -39,23 +35,23 @@ struct Translator static Letter getReverseComplement(Letter letter) { return reverseLetter[(int) letter]; } - static Letter getAminoAcid(vector const &dnaSequence, size_t pos) + static Letter getAminoAcid(const Sequence& dnaSequence, size_t pos) { return lookup[(int) dnaSequence[pos]][(int)dnaSequence[pos+1]][(int)dnaSequence[pos+2]]; } - static Letter getAminoAcidReverse(vector const &dnaSequence, size_t pos) + static Letter getAminoAcidReverse(const Sequence& dnaSequence, size_t pos) { return lookupReverse[(int) dnaSequence[pos + 2]][(int)dnaSequence[pos + 1]][(int)dnaSequence[pos]]; } - static vector reverse(const vector &seq) + static std::vector reverse(const std::vector &seq) { - vector r; - for(vector::const_reverse_iterator i=seq.rbegin(); i!=seq.rend(); ++i) + std::vector r; + for(std::vector::const_reverse_iterator i=seq.rbegin(); i!=seq.rend(); ++i) r.push_back(getReverseComplement(*i)); return r; } - static size_t translate(vector const &dnaSequence, vector *proteins) + static size_t translate(const Sequence& dnaSequence, std::vector *proteins) { - size_t length_ = dnaSequence.size(), d, n; + size_t length_ = dnaSequence.length(), d, n; proteins[0].resize(d = length_ / 3); proteins[3].resize(d); n = 2*d; @@ -96,7 +92,7 @@ struct Translator return p; } - static void mask_runs(vector &query, unsigned run_len) + static void mask_runs(std::vector &query, unsigned run_len) { Letter *last = &query[0]-1, *i = &query[0], *end = &query.back(); while (i <= end) { @@ -115,7 +111,7 @@ struct Translator } } - static unsigned computeGoodFrames(vector const *queries, unsigned runLen) + static unsigned computeGoodFrames(std::vector const *queries, unsigned runLen) { unsigned set = 0; @@ -139,7 +135,7 @@ struct Translator return set; } - static void mask_runs(vector *queries, unsigned run_len) + static void mask_runs(std::vector *queries, unsigned run_len) { for (unsigned i = 0; i < 6; ++i) mask_runs(queries[i], run_len); @@ -147,4 +143,3 @@ struct Translator }; -#endif /* TRANSLATE_H_ */ diff --git a/src/util/simd.cpp b/src/util/simd.cpp index 109ae8a76..880c8db65 100644 --- a/src/util/simd.cpp +++ b/src/util/simd.cpp @@ -62,6 +62,10 @@ Arch init_arch() { cpuid(info, 7); if ((info[1] & (1 << 5)) != 0) flags |= AVX2; +#ifdef WITH_AVX512 + if ((info[1] & (1 << 16)) != 0 && (info[1] & (1 << 30)) != 0) + flags |= AVX512; +#endif } #endif @@ -82,6 +86,8 @@ Arch init_arch() { throw std::runtime_error("CPU does not support AVX2. Please compile the software from source."); #endif + if (flags & AVX512) + return Arch::AVX512; if ((flags & SSSE3) && (flags & POPCNT) && (flags & SSE4_1) && (flags & AVX2)) return Arch::AVX2; if ((flags & SSSE3) && (flags & POPCNT) && (flags & SSE4_1)) diff --git a/src/util/simd.h b/src/util/simd.h index 2eca6e876..dc339e44c 100644 --- a/src/util/simd.h +++ b/src/util/simd.h @@ -47,8 +47,8 @@ along with this program. If not, see . namespace SIMD { -enum class Arch { None, Generic, SSE4_1, AVX2 }; -enum Flags { SSSE3 = 1, POPCNT = 2, SSE4_1 = 4, AVX2 = 8 }; +enum class Arch { None, Generic, SSE4_1, AVX2, AVX512 }; +enum Flags { SSSE3 = 1, POPCNT = 2, SSE4_1 = 4, AVX2 = 8, AVX512 = 16 }; Arch arch(); std::string features(); @@ -62,50 +62,23 @@ struct Vector {}; }} -namespace SIMD { - -#ifdef __SSE2__ - -static inline __m128i _mm_set1_epi8(char v) { #ifdef __APPLE__ - return _mm_set_epi8(v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v); -#else - return ::_mm_set1_epi8(v); -#endif -} - -static inline __m128i _mm_set1_epi16(short v) { -#ifdef __APPLE__ - return _mm_set_epi16(v, v, v, v, v, v, v, v); -#else - return ::_mm_set1_epi16(v); -#endif -} - +#ifdef __SSE2__ +#define _mm_set1_epi8(v) _mm_set_epi8(v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v) +#define _mm_set1_epi16(v) _mm_set_epi16(v, v, v, v, v, v, v, v) #endif - #ifdef __AVX2__ - -static inline __m256i _mm256_set1_epi8(char v) { -#ifdef __APPLE__ - return _mm256_set_epi8(v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v); -#else - return ::_mm256_set1_epi8(v); +#define _mm256_set1_epi8(v) _mm256_set_epi8(v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v) +#define _mm256_set1_epi16(v) _mm256_set_epi16(v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v) #endif -} - -static inline __m256i _mm256_set1_epi16(short v) { -#ifdef __APPLE__ - return _mm256_set_epi16(v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v); -#else - return ::_mm256_set1_epi16(v); #endif -} #if defined(__GNUC__) && __GNUC__ < 8 #define _mm256_set_m128i(v0, v1) _mm256_insertf128_si256(_mm256_castsi128_si256(v1), (v0), 1) #endif +#ifdef __AVX2__ + inline void print_8(__m256i x, std::ostream& s) { alignas(32) int8_t v[32]; _mm256_store_si256((__m256i*)v, x); @@ -122,8 +95,6 @@ inline void print_16(__m256i x, std::ostream& s) { #endif -} - #ifdef __SSE__ #if defined(__GNUC__) && !defined(__clang__) && defined(__SSE__) @@ -135,17 +106,36 @@ inline void print_16(__m256i x, std::ostream& s) { #include +#ifdef WITH_AVX512 + #define DECL_DISPATCH(ret, name, param) namespace ARCH_GENERIC { ret name param; }\ namespace ARCH_SSE4_1 { ret name param; }\ namespace ARCH_AVX2 { ret name param; }\ +namespace ARCH_AVX512 { ret name param; }\ static inline std::function dispatch_target_##name() {\ switch(::SIMD::arch()) {\ case ::SIMD::Arch::SSE4_1: return ARCH_SSE4_1::name;\ case ::SIMD::Arch::AVX2: return ARCH_AVX2::name;\ +case ::SIMD::Arch::AVX512: return ARCH_AVX512::name;\ default: return ARCH_GENERIC::name;\ }}\ const std::function name = dispatch_target_##name(); +#else + +#define DECL_DISPATCH(ret, name, param) namespace ARCH_GENERIC { ret name param; }\ +namespace ARCH_SSE4_1 { ret name param; }\ +namespace ARCH_AVX2 { ret name param; }\ +static inline std::function dispatch_target_##name() {\ +switch(::SIMD::arch()) {\ +case ::SIMD::Arch::SSE4_1: return ARCH_SSE4_1::name;\ +case ::SIMD::Arch::AVX2: return ARCH_AVX2::name;\ +default: return ARCH_GENERIC::name;\ +}}\ +const std::function name = dispatch_target_##name(); + +#endif + #if defined(__GNUC__) && !defined(__clang__) && defined(__SSE__) #pragma GCC pop_options #elif defined(__clang__) && defined(__SSE__) diff --git a/src/util/simd/transpose.h b/src/util/simd/transpose.h index e7a2f729a..84c429585 100644 --- a/src/util/simd/transpose.h +++ b/src/util/simd/transpose.h @@ -27,4 +27,8 @@ along with this program. If not, see . #if ARCH_ID == 2 #include "transpose32x32.h" +#endif + +#if ARCH_ID == 3 +static inline void transpose(const signed char** data, size_t n, signed char* out, const __m512i&) {} #endif \ No newline at end of file diff --git a/src/util/simd/transpose16x16.h b/src/util/simd/transpose16x16.h index 2db1a0c87..f98e85754 100644 --- a/src/util/simd/transpose16x16.h +++ b/src/util/simd/transpose16x16.h @@ -26,6 +26,11 @@ along with this program. If not, see . #define UNPACK128_LO_HI_EPI32(a, b) t = r##a; r##a = _mm_unpacklo_epi32(t, r##b); r##b = _mm_unpackhi_epi32(t, r##b); #define UNPACK128_LO_HI_EPI64(a, b) t = r##a; r##a = _mm_unpacklo_epi64(t, r##b); r##b = _mm_unpackhi_epi64(t, r##b); +#define UNPACK256_LO_HI_EPI16(a, b) t = r##a; r##a = _mm256_unpacklo_epi16(t, r##b); r##b = _mm256_unpackhi_epi16(t, r##b); +#define UNPACK256_LO_HI_EPI32(a, b) t = r##a; r##a = _mm256_unpacklo_epi32(t, r##b); r##b = _mm256_unpackhi_epi32(t, r##b); +#define UNPACK256_LO_HI_EPI64(a, b) t = r##a; r##a = _mm256_unpacklo_epi64(t, r##b); r##b = _mm256_unpackhi_epi64(t, r##b); +#define UNPACK256_LO_HI_EPI128(a, b) t = r##a; r##a = _mm256_permute2x128_si256(t, r##b, 32); r##b = _mm256_permute2x128_si256(t, r##b, 49); + static inline void transpose(const signed char **data, size_t n, signed char *out, const __m128i&) { __m128i r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, t; r0 = r1 = r2 = r3 = r4 = r5 = r6 = r7 = r8 = r9 = r10 = r11 = r12 = r13 = r14 = r15 = _mm_setzero_si128(); @@ -118,4 +123,180 @@ static inline void transpose(const signed char **data, size_t n, signed char *ou _mm_store_si128(ptr++, r11); _mm_store_si128(ptr++, r7); _mm_store_si128(ptr, r15); -} \ No newline at end of file +} + +#ifdef __AVX2__ + +static inline void transpose_offset(const int16_t** data, size_t n, ptrdiff_t offset, int16_t* out, __m256i) { + __m256i r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, t; + + r0 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r1 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r2 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r3 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r4 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r5 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r6 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r7 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r8 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r9 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r10 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r11 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r12 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r13 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r14 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + r15 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + + UNPACK256_LO_HI_EPI16(0, 1) + UNPACK256_LO_HI_EPI16(2, 3) + UNPACK256_LO_HI_EPI16(4, 5) + UNPACK256_LO_HI_EPI16(6, 7) + UNPACK256_LO_HI_EPI16(8, 9) + UNPACK256_LO_HI_EPI16(10, 11) + UNPACK256_LO_HI_EPI16(12, 13) + UNPACK256_LO_HI_EPI16(14, 15) + + UNPACK256_LO_HI_EPI32(0, 2) + UNPACK256_LO_HI_EPI32(1, 3) + UNPACK256_LO_HI_EPI32(4, 6) + UNPACK256_LO_HI_EPI32(5, 7) + UNPACK256_LO_HI_EPI32(8, 10) + UNPACK256_LO_HI_EPI32(9, 11) + UNPACK256_LO_HI_EPI32(12, 14) + UNPACK256_LO_HI_EPI32(13, 15) + + UNPACK256_LO_HI_EPI64(0, 4) + UNPACK256_LO_HI_EPI64(2, 6) + UNPACK256_LO_HI_EPI64(1, 5) + UNPACK256_LO_HI_EPI64(3, 7) + UNPACK256_LO_HI_EPI64(8, 12) + UNPACK256_LO_HI_EPI64(10, 14) + UNPACK256_LO_HI_EPI64(9, 13) + UNPACK256_LO_HI_EPI64(11, 15) + + UNPACK256_LO_HI_EPI128(0, 8) + UNPACK256_LO_HI_EPI128(4, 12) + UNPACK256_LO_HI_EPI128(2, 10) + UNPACK256_LO_HI_EPI128(6, 14) + UNPACK256_LO_HI_EPI128(1, 9) + UNPACK256_LO_HI_EPI128(5, 13) + UNPACK256_LO_HI_EPI128(3, 11) + UNPACK256_LO_HI_EPI128(7, 15) + + __m256i* ptr = (__m256i*)out; + _mm256_store_si256(ptr++, r0); + _mm256_store_si256(ptr++, r4); + _mm256_store_si256(ptr++, r2); + _mm256_store_si256(ptr++, r6); + _mm256_store_si256(ptr++, r1); + _mm256_store_si256(ptr++, r5); + _mm256_store_si256(ptr++, r3); + _mm256_store_si256(ptr++, r7); + _mm256_store_si256(ptr++, r8); + _mm256_store_si256(ptr++, r12); + _mm256_store_si256(ptr++, r10); + _mm256_store_si256(ptr++, r14); + _mm256_store_si256(ptr++, r9); + _mm256_store_si256(ptr++, r13); + _mm256_store_si256(ptr++, r11); + _mm256_store_si256(ptr++, r15); +} + +#define LOAD(r) r = _mm256_loadu_si256((const __m256i*) * (data++) + offset); +#define ALIGN(r) r = _mm256_unpacklo_epi8(_mm256_permute4x64_epi64(r, 2),z); + +static inline void transpose_offset_8bit(const int16_t** data, size_t n, ptrdiff_t offset, int16_t* out, __m256i) { + __m256i r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, t; + //__m128i s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15; + __m256i z = _mm256_setzero_si256(); + + LOAD(r0) + LOAD(r1) + LOAD(r2) + LOAD(r3) + LOAD(r4) + LOAD(r5) + LOAD(r6) + LOAD(r7) + LOAD(r8) + LOAD(r9) + LOAD(r10) + LOAD(r11) + LOAD(r12) + LOAD(r13) + LOAD(r14) + LOAD(r15) + + ALIGN(r0) + ALIGN(r1) + ALIGN(r2) + ALIGN(r3) + ALIGN(r4) + ALIGN(r5) + ALIGN(r6) + ALIGN(r7) + ALIGN(r8) + ALIGN(r9) + ALIGN(r10) + ALIGN(r11) + ALIGN(r12) + ALIGN(r13) + ALIGN(r14) + ALIGN(r15) + + UNPACK256_LO_HI_EPI16(0, 1) + UNPACK256_LO_HI_EPI16(2, 3) + UNPACK256_LO_HI_EPI16(4, 5) + UNPACK256_LO_HI_EPI16(6, 7) + UNPACK256_LO_HI_EPI16(8, 9) + UNPACK256_LO_HI_EPI16(10, 11) + UNPACK256_LO_HI_EPI16(12, 13) + UNPACK256_LO_HI_EPI16(14, 15) + + UNPACK256_LO_HI_EPI32(0, 2) + UNPACK256_LO_HI_EPI32(1, 3) + UNPACK256_LO_HI_EPI32(4, 6) + UNPACK256_LO_HI_EPI32(5, 7) + UNPACK256_LO_HI_EPI32(8, 10) + UNPACK256_LO_HI_EPI32(9, 11) + UNPACK256_LO_HI_EPI32(12, 14) + UNPACK256_LO_HI_EPI32(13, 15) + + UNPACK256_LO_HI_EPI64(0, 4) + UNPACK256_LO_HI_EPI64(2, 6) + UNPACK256_LO_HI_EPI64(1, 5) + UNPACK256_LO_HI_EPI64(3, 7) + UNPACK256_LO_HI_EPI64(8, 12) + UNPACK256_LO_HI_EPI64(10, 14) + UNPACK256_LO_HI_EPI64(9, 13) + UNPACK256_LO_HI_EPI64(11, 15) + + UNPACK256_LO_HI_EPI128(0, 8) + UNPACK256_LO_HI_EPI128(4, 12) + UNPACK256_LO_HI_EPI128(2, 10) + UNPACK256_LO_HI_EPI128(6, 14) + UNPACK256_LO_HI_EPI128(1, 9) + UNPACK256_LO_HI_EPI128(5, 13) + UNPACK256_LO_HI_EPI128(3, 11) + UNPACK256_LO_HI_EPI128(7, 15) + + __m256i* ptr = (__m256i*)out; + _mm256_store_si256(ptr++, r0); + _mm256_store_si256(ptr++, r4); + _mm256_store_si256(ptr++, r2); + _mm256_store_si256(ptr++, r6); + _mm256_store_si256(ptr++, r1); + _mm256_store_si256(ptr++, r5); + _mm256_store_si256(ptr++, r3); + _mm256_store_si256(ptr++, r7); + _mm256_store_si256(ptr++, r8); + _mm256_store_si256(ptr++, r12); + _mm256_store_si256(ptr++, r10); + _mm256_store_si256(ptr++, r14); + _mm256_store_si256(ptr++, r9); + _mm256_store_si256(ptr++, r13); + _mm256_store_si256(ptr++, r11); + _mm256_store_si256(ptr++, r15); +} + +#endif \ No newline at end of file diff --git a/src/util/simd/transpose32x32.h b/src/util/simd/transpose32x32.h index 65149a8a7..f3ed5a112 100644 --- a/src/util/simd/transpose32x32.h +++ b/src/util/simd/transpose32x32.h @@ -184,4 +184,166 @@ static inline void transpose(const signed char** data, size_t n, signed char* ou _mm256_store_si256(ptr++, r27); _mm256_store_si256(ptr++, r23); _mm256_store_si256(ptr++, r31); +} + +static inline void transpose_offset(const signed char** data, size_t n, ptrdiff_t offset, signed char* out, __m256i) { + __m256i r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, r30, r31, t; + r0 = r1 = r2 = r3 = r4 = r5 = r6 = r7 = r8 = r9 = r10 = r11 = r12 = r13 = r14 = r15 = r16 = r17 = r18 = r19 = r20 = r21 = r22 = r23 = r24 = r25 = r26 = r27 = r28 = r29 = r30 = r31 = _mm256_setzero_si256(); + + switch (n) { + case 32: r0 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 31: r1 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 30: r2 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 29: r3 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 28: r4 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 27: r5 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 26: r6 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 25: r7 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 24: r8 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 23: r9 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 22: r10 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 21: r11 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 20: r12 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 19: r13 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 18: r14 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 17: r15 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 16: r16 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 15: r17 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 14: r18 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 13: r19 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 12: r20 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 11: r21 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 10: r22 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 9: r23 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 8: r24 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 7: r25 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 6: r26 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 5: r27 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 4: r28 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 3: r29 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 2: r30 = _mm256_loadu_si256((const __m256i*) * (data++) + offset); + case 1: r31 = _mm256_loadu_si256((const __m256i*) * data + offset); + } + + UNPACK256_LO_HI_EPI8(0, 1) + UNPACK256_LO_HI_EPI8(2, 3) + UNPACK256_LO_HI_EPI8(4, 5) + UNPACK256_LO_HI_EPI8(6, 7) + UNPACK256_LO_HI_EPI8(8, 9) + UNPACK256_LO_HI_EPI8(10, 11) + UNPACK256_LO_HI_EPI8(12, 13) + UNPACK256_LO_HI_EPI8(14, 15) + UNPACK256_LO_HI_EPI8(16, 17) + UNPACK256_LO_HI_EPI8(18, 19) + UNPACK256_LO_HI_EPI8(20, 21) + UNPACK256_LO_HI_EPI8(22, 23) + UNPACK256_LO_HI_EPI8(24, 25) + UNPACK256_LO_HI_EPI8(26, 27) + UNPACK256_LO_HI_EPI8(28, 29) + UNPACK256_LO_HI_EPI8(30, 31) + + UNPACK256_LO_HI_EPI16(0, 2) + UNPACK256_LO_HI_EPI16(1, 3) + UNPACK256_LO_HI_EPI16(4, 6) + UNPACK256_LO_HI_EPI16(5, 7) + UNPACK256_LO_HI_EPI16(8, 10) + UNPACK256_LO_HI_EPI16(9, 11) + UNPACK256_LO_HI_EPI16(12, 14) + UNPACK256_LO_HI_EPI16(13, 15) + UNPACK256_LO_HI_EPI16(16, 18) + UNPACK256_LO_HI_EPI16(17, 19) + UNPACK256_LO_HI_EPI16(20, 22) + UNPACK256_LO_HI_EPI16(21, 23) + UNPACK256_LO_HI_EPI16(24, 26) + UNPACK256_LO_HI_EPI16(25, 27) + UNPACK256_LO_HI_EPI16(28, 30) + UNPACK256_LO_HI_EPI16(29, 31) + + UNPACK256_LO_HI_EPI32(0, 4) + UNPACK256_LO_HI_EPI32(2, 6) + UNPACK256_LO_HI_EPI32(1, 5) + UNPACK256_LO_HI_EPI32(3, 7) + UNPACK256_LO_HI_EPI32(8, 12) + UNPACK256_LO_HI_EPI32(10, 14) + UNPACK256_LO_HI_EPI32(9, 13) + UNPACK256_LO_HI_EPI32(11, 15) + UNPACK256_LO_HI_EPI32(16, 20) + UNPACK256_LO_HI_EPI32(18, 22) + UNPACK256_LO_HI_EPI32(17, 21) + UNPACK256_LO_HI_EPI32(19, 23) + UNPACK256_LO_HI_EPI32(24, 28) + UNPACK256_LO_HI_EPI32(26, 30) + UNPACK256_LO_HI_EPI32(25, 29) + UNPACK256_LO_HI_EPI32(27, 31) + + UNPACK256_LO_HI_EPI64(0, 8) + UNPACK256_LO_HI_EPI64(4, 12) + UNPACK256_LO_HI_EPI64(2, 10) + UNPACK256_LO_HI_EPI64(6, 14) + UNPACK256_LO_HI_EPI64(1, 9) + UNPACK256_LO_HI_EPI64(5, 13) + UNPACK256_LO_HI_EPI64(3, 11) + UNPACK256_LO_HI_EPI64(7, 15) + UNPACK256_LO_HI_EPI64(16, 24) + UNPACK256_LO_HI_EPI64(20, 28) + UNPACK256_LO_HI_EPI64(18, 26) + UNPACK256_LO_HI_EPI64(22, 30) + UNPACK256_LO_HI_EPI64(17, 25) + UNPACK256_LO_HI_EPI64(21, 29) + UNPACK256_LO_HI_EPI64(19, 27) + UNPACK256_LO_HI_EPI64(23, 31) + + UNPACK256_LO_HI_EPI128(0, 16) + UNPACK256_LO_HI_EPI128(8, 24) + UNPACK256_LO_HI_EPI128(4, 20) + UNPACK256_LO_HI_EPI128(12, 28) + UNPACK256_LO_HI_EPI128(2, 18) + UNPACK256_LO_HI_EPI128(10, 26) + UNPACK256_LO_HI_EPI128(6, 22) + UNPACK256_LO_HI_EPI128(14, 30) + UNPACK256_LO_HI_EPI128(1, 17) + UNPACK256_LO_HI_EPI128(9, 25) + UNPACK256_LO_HI_EPI128(5, 21) + UNPACK256_LO_HI_EPI128(13, 29) + UNPACK256_LO_HI_EPI128(3, 19) + UNPACK256_LO_HI_EPI128(11, 27) + UNPACK256_LO_HI_EPI128(7, 23) + UNPACK256_LO_HI_EPI128(15, 31) + + __m256i* ptr = (__m256i*)out; + _mm256_store_si256(ptr++, r0); + _mm256_store_si256(ptr++, r8); + _mm256_store_si256(ptr++, r4); + _mm256_store_si256(ptr++, r12); + _mm256_store_si256(ptr++, r2); + _mm256_store_si256(ptr++, r10); + _mm256_store_si256(ptr++, r6); + _mm256_store_si256(ptr++, r14); + _mm256_store_si256(ptr++, r1); + _mm256_store_si256(ptr++, r9); + _mm256_store_si256(ptr++, r5); + _mm256_store_si256(ptr++, r13); + _mm256_store_si256(ptr++, r3); + _mm256_store_si256(ptr++, r11); + _mm256_store_si256(ptr++, r7); + _mm256_store_si256(ptr++, r15); + _mm256_store_si256(ptr++, r16); + _mm256_store_si256(ptr++, r24); + _mm256_store_si256(ptr++, r20); + _mm256_store_si256(ptr++, r28); + _mm256_store_si256(ptr++, r18); + _mm256_store_si256(ptr++, r26); + _mm256_store_si256(ptr++, r22); + _mm256_store_si256(ptr++, r30); + _mm256_store_si256(ptr++, r17); + _mm256_store_si256(ptr++, r25); + _mm256_store_si256(ptr++, r21); + _mm256_store_si256(ptr++, r29); + _mm256_store_si256(ptr++, r19); + _mm256_store_si256(ptr++, r27); + _mm256_store_si256(ptr++, r23); + _mm256_store_si256(ptr++, r31); +} + +static inline void transpose_offset2(const signed char** data, size_t n, ptrdiff_t offset, signed char* out, __m256i) { } \ No newline at end of file diff --git a/src/util/simd/vector.h b/src/util/simd/vector.h index 3e3b4bafb..ff4c36cb1 100644 --- a/src/util/simd/vector.h +++ b/src/util/simd/vector.h @@ -23,7 +23,9 @@ along with this program. If not, see . #include "../simd.h" -#if ARCH_ID == 2 +#if ARCH_ID == 3 +#include "vector8_avx512.h" +#elif ARCH_ID == 2 #include "vector8_avx2.h" #elif defined(__SSE2__) #include "vector8_sse.h" diff --git a/src/util/simd/vector8_avx512.h b/src/util/simd/vector8_avx512.h new file mode 100644 index 000000000..2c61f7536 --- /dev/null +++ b/src/util/simd/vector8_avx512.h @@ -0,0 +1,87 @@ +/**** +DIAMOND protein aligner +Copyright (C) 2020 Max Planck Society for the Advancement of Science e.V. + +Code developed by Benjamin Buchfink + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +****/ + +#pragma once +#include +#include "../simd.h" + +namespace DISPATCH_ARCH { namespace SIMD { + +template<> +struct Vector { + + static constexpr size_t CHANNELS = 32; + + Vector() + {} + + Vector(const signed char* p) : + v(_mm512_loadu_si512((const __m512i*)p)) + {} + + operator __m512i() const { + return v; + } + + __m512i v; + +}; + +template<> +struct Vector { + + static constexpr size_t CHANNELS = 16; + + Vector() + {} + + Vector(const int16_t* p) : + v(_mm256_loadu_si256((const __m256i*)p)) + {} + + operator __m256i() const { + return v; + } + + __m256i v; + +}; + +template<> +struct Vector { + + static constexpr size_t CHANNELS = 1; + + Vector() + {} + + Vector(const int32_t* p) : + v(*p) + {} + + operator int32_t() const { + return v; + } + + int32_t v; + +}; + +}} \ No newline at end of file diff --git a/src/util/string/fixed_string.h b/src/util/string/fixed_string.h new file mode 100644 index 000000000..d28a7fe4b --- /dev/null +++ b/src/util/string/fixed_string.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include "../algo/MurmurHash3.h" + +extern std::array fixed_string_seed; + +template +struct FixedString { + FixedString(const std::string& s) { + if (s.length() >= L) + throw std::runtime_error("FixedString"); + std::copy(s.begin(), s.end(), chars.begin()); + chars[s.length()] = '\0'; + } + bool operator==(const FixedString& s) const { + return strcmp(chars.data(), s.chars.data()) == 0; + } + std::array chars; + struct Hash { + size_t operator()(const FixedString& s) const { + std::array out; + MurmurHash3_x64_128(s.chars.data(), (int)strlen(s.chars.data()), fixed_string_seed.data(), out.data()); + return *(size_t*)out.data(); + //return std::hash()(string(s.chars.data())); + } + }; +}; diff --git a/src/util/string/string.cpp b/src/util/string/string.cpp index c0f35cf9c..9d593eb16 100644 --- a/src/util/string/string.cpp +++ b/src/util/string/string.cpp @@ -1,6 +1,7 @@ #include #include #include "string.h" +#include "fixed_string.h" using std::string; using std::to_string; @@ -8,6 +9,8 @@ using std::stringstream; using std::setprecision; using std::fixed; +std::array fixed_string_seed; + string convert_size(size_t size) { static const char *SIZES[] = { "B", "KB", "MB", "GB" }; size_t div = 0; @@ -46,4 +49,20 @@ std::string ratio_percentage(const size_t x, const size_t y) { return ratio_percentage((double)x, (double)y); } +int64_t interpret_number(const std::string& s) { + stringstream ss(s); + double n; + ss >> n; + char c = 0; + ss >> c; + if (ss.eof()) + throw std::runtime_error("Missing size specifier in number: " + s + ". Permitted values: 'G'"); + if (c != 'G') + throw std::runtime_error(string("Invalid size specifier (") + c + ") in number: " + s + ". Permitted values: 'G'"); + ss >> c; + if (!ss.eof()) + throw std::runtime_error("Invalid number format: " + s); + return int64_t(n * 1e9); +} + }} \ No newline at end of file diff --git a/src/util/string/string.h b/src/util/string/string.h index 945cb6bd5..07e890e3b 100644 --- a/src/util/string/string.h +++ b/src/util/string/string.h @@ -9,16 +9,17 @@ #include inline bool ends_with(const std::string &s, const char *t) { - const size_t l = strlen(t); - return s.length() >= l && strncmp(&s[s.length() - l], t, l) == 0; + if (s.length() < strlen(t)) + return false; + return s.compare(s.length() - strlen(t), std::string::npos, t) == 0; } -inline std::string& rstrip(std::string &s, const char *t) { +inline std::string rstrip(const std::string &s, const char *t) { const size_t l = strlen(t); if (s.length() < l) return s; if (s.compare(s.length() - l, std::string::npos, t) == 0) - return s.erase(s.length() - l, std::string::npos); + return std::string(s.begin(), s.end() - l); else return s; } @@ -56,5 +57,6 @@ inline int format_double(double x, char *p) { std::string replace(const std::string& s, char a, char b); std::string ratio_percentage(const double x, const double y); std::string ratio_percentage(const size_t x, const size_t y); +int64_t interpret_number(const std::string& s); }} diff --git a/src/util/string/tokenizer.h b/src/util/string/tokenizer.h index 6a3423424..a54023deb 100644 --- a/src/util/string/tokenizer.h +++ b/src/util/string/tokenizer.h @@ -82,11 +82,12 @@ struct Tokenizer { return *this; } - Tokenizer& operator>>(long int &x) { + Tokenizer& operator>>(int64_t &x) { if (!good()) throw TokenizerException(); char *end; - x = strtol(p, &end, 10); + const long long n = strtoll(p, &end, 10); + x = (int64_t)n; if (end == p) throw TokenizerException(); if (strncmp(end, delimiter, len) == 0) @@ -100,14 +101,14 @@ struct Tokenizer { } Tokenizer& operator>>(int &x) { - long y; + int64_t y; *this >> y; x = (int)y; return *this; } Tokenizer& operator>>(unsigned& x) { - long y; + int64_t y; *this >> y; x = (unsigned)y; return *this; diff --git a/src/util/string/tsv.h b/src/util/string/tsv.h deleted file mode 100644 index 9ada1f789..000000000 --- a/src/util/string/tsv.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include "../io/text_input_file.h" - -namespace Util { namespace Tsv { - -std::string fetch_block(TextInputFile& f, std::string& buf); -std::string column(const std::string& line, const size_t i); -std::string columns(const std::string& line, const size_t begin, const size_t end); -size_t column_count(const std::string& line); -std::vector extract_column(const std::string& buf, const size_t i); - -}} \ No newline at end of file diff --git a/src/util/system.h b/src/util/system.h index d36f48e75..88a837981 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -25,15 +25,20 @@ along with this program. If not, see . #ifdef _MSC_VER +typedef __int64 ssize_t; +#define STDIN_FILENO _fileno(stdin) +#define STDOUT_FILENO _fileno(stdout) #define PACKED_ATTRIBUTE #define FORCE_INLINE __forceinline static #define FLATTEN +#define UNLINK _unlink #else #define PACKED_ATTRIBUTE __attribute__((packed)) #define FORCE_INLINE __attribute__((always_inline)) static inline #define FLATTEN __attribute__((flatten)) +#define UNLINK unlink #endif diff --git a/src/util/system/endianness.h b/src/util/system/endianness.h index 8c1db7324..70acf354d 100644 --- a/src/util/system/endianness.h +++ b/src/util/system/endianness.h @@ -32,4 +32,22 @@ inline uint16_t big_endian_byteswap(uint16_t x) return psnip_endian_le16(x); } +template<> +inline int64_t big_endian_byteswap(int64_t x) +{ + return psnip_endian_le64(x); +} + +template<> +inline int32_t big_endian_byteswap(int32_t x) +{ + return psnip_endian_le32(x); +} + +template<> +inline int16_t big_endian_byteswap(int16_t x) +{ + return psnip_endian_le16(x); +} + #endif \ No newline at end of file diff --git a/src/util/table.h b/src/util/table.h index b946ac44c..f5c229fc2 100644 --- a/src/util/table.h +++ b/src/util/table.h @@ -6,6 +6,8 @@ #include #include +namespace Util { + struct Table { Table(): @@ -18,8 +20,14 @@ struct Table { return *this; } - Table& operator()(const std::string& s, size_t n) { - data_.emplace_back(s, std::to_string(n)); + Table& operator()(const std::string& s, int64_t n, const char* unit = "") { + data_.emplace_back(s, std::to_string(n) + unit); + max_len_ = std::max(max_len_, s.length()); + return *this; + } + + Table& operator()(const std::string& s, uint64_t n, const char* unit = "") { + data_.emplace_back(s, std::to_string(n) + unit); max_len_ = std::max(max_len_, s.length()); return *this; } @@ -41,4 +49,6 @@ struct Table { std::vector> data_; size_t max_len_; -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/util/text_buffer.h b/src/util/text_buffer.h index b1cd020a2..16b34ac15 100644 --- a/src/util/text_buffer.h +++ b/src/util/text_buffer.h @@ -206,6 +206,20 @@ struct TextBuffer return *this; } + TextBuffer& operator<<(long x) + { + reserve(32); + ptr_ += sprintf(ptr_, "%li", x); + return *this; + } + + TextBuffer& operator<<(long long x) + { + reserve(32); + ptr_ += sprintf(ptr_, "%lli", x); + return *this; + } + TextBuffer& operator<<(double x) { reserve(32); @@ -238,20 +252,21 @@ struct TextBuffer return *this; } - TextBuffer& print(const std::vector &v, char separator) + template + TextBuffer& print(const std::vector &v, char separator) { if (v.empty()) return *this; - std::vector::const_iterator i = v.begin(); + typename std::vector::const_iterator i = v.begin(); *this << *(i++); for (; i < v.end(); ++i) *this << ';' << *i; return *this; } - template - TextBuffer& operator<<(const std::vector<_t> &v) + template + TextBuffer& operator<<(const std::vector &v) { - const size_t l = v.size() * sizeof(_t); + const size_t l = v.size() * sizeof(T); reserve(l); memcpy(ptr_, v.data(), l); ptr_ += l; diff --git a/src/util/tsv/construct.h b/src/util/tsv/construct.h new file mode 100644 index 000000000..cd55fa511 --- /dev/null +++ b/src/util/tsv/construct.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include "def.h" + +namespace Util { namespace Tsv { + +template +struct Construct { + template + T operator()(Tok& tok, Uargs... args) { + const T1 v = convert_string(*tok); + ++tok; + return Construct().template operator()(tok, args..., v); + } +}; + +template<> +struct Construct { + template + T operator()(Tok&, Uargs... args) { + return T(args...); + } +}; + +}} \ No newline at end of file diff --git a/src/util/tsv/def.h b/src/util/tsv/def.h new file mode 100644 index 000000000..1ec50e252 --- /dev/null +++ b/src/util/tsv/def.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include + +namespace Util { namespace Tsv { + +enum class Type { + STRING, INT64 +}; + +using Schema = std::vector; +using RecordId = int64_t; + +struct InvalidType : public std::runtime_error { + InvalidType() : + std::runtime_error("Invalid type in schema.") + {} +}; + +struct SchemaMismatch : public std::runtime_error { + SchemaMismatch() : + std::runtime_error("Mismatching schema.") + {} +}; + +}} diff --git a/src/util/tsv/file.cpp b/src/util/tsv/file.cpp new file mode 100644 index 000000000..4ad18b2ca --- /dev/null +++ b/src/util/tsv/file.cpp @@ -0,0 +1,149 @@ +#include "file.h" +#include "../enum.h" +#include "construct.h" +#include "../data_structures/reorder_queue.h" + +using std::vector; +using std::function; +using std::string; +using std::runtime_error; + +namespace Util { namespace Tsv { + +static Flags get_flags(Flags flags) { + if (flag_any(flags, Flags::TEMP)) { + if (flag_any(flags, Flags::WRITE)) + throw runtime_error("Write-only temp file."); + flags |= Flags::READ_WRITE | Flags::OVERWRITE; + } + return flags; +} + +File::File(const Schema& schema, const char* file_name, Flags flags, const Config& config) : + flags_(get_flags(flags)), + schema_(schema), + config_(config), + out_file_(flag_any(flags_, Flags::WRITE | Flags::READ_WRITE) ? (flag_any(flags_, Flags::TEMP) ? new TempFile : new OutputFile(file_name, Compressor::NONE, flag_any(flags_, Flags::OVERWRITE) ? "w+b" : "r+b")) : nullptr), + file_(flag_any(flags_, Flags::READ_WRITE) ? (flag_any(flags_, Flags::TEMP) ? new TextInputFile(*(TempFile*)out_file_.get()) : new TextInputFile(*out_file_)) : (flag_any(flags_, Flags::WRITE) ? nullptr : new TextInputFile(file_name))), + record_id_(0) +{ + if (flag_any(flags_, Flags::RECORD_ID_COLUMN) && schema.front() != Type::INT64) + throw runtime_error("Schema does not contain record_id column."); +} + +File::File(const Schema& schema, const std::string& file_name, Flags flags, const Config& config): + File(schema, file_name.c_str(), flags, config) +{} + +void File::rewind() { + if (out_file_) + out_file_->rewind(); + if (file_) + file_->rewind(); + record_id_ = 0; +} + +File::~File() { + if (file_ && file_->temp_file) + file_->close_and_delete(); + else { + if (out_file_) + out_file_->close(); + else + file_->close(); + } +} + +void File::read(int threads, std::function& callback) { + function f([this, &callback](int64_t chunk, const char* begin, const char* end) { + Table table(schema_); + table.append(begin, end); + callback(chunk, table); + }); + read(threads, f); +} + +Table File::read(int threads) { + Table table(schema_); + function f([this](const char* begin, const char* end) { + Table table(this->schema()); + LineIterator it(begin, end); + while (it.good()) { + const string line = *it; + table.push_back(line.begin(), line.end()); + ++it; + } + }); + rewind(); + while(file_->getline(), (!file_->eof() || !file_->line.empty())) { + table.push_back(file_->line.cbegin(), file_->line.cend()); + } + return table; +} + +Table File::read(int64_t max_records, int threads) { + Table table(schema_); + while (table.size() < max_records && (file_->getline(), (!file_->eof() || !file_->line.empty()))) { + table.push_back(file_->line.cbegin(), file_->line.cend(), flag_any(flags_, Flags::RECORD_ID_COLUMN) ? record_id_ : -1); + ++record_id_; + } + return table; +} + +void File::write_record(int i) { + if (i != (flag_any(flags_, Flags::RECORD_ID_COLUMN) ? 1 : 0)) + throw runtime_error("write_record with insufficient field count."); + write_buf_ << '\n'; + out_file_->write(write_buf_.data(), write_buf_.size()); + write_buf_.clear(); +} + +void File::write(const Record& record) { + record.write(write_buf_); + out_file_->write(write_buf_.data(), write_buf_.size()); + write_buf_.clear(); +} + +void File::write(const Table& table) { + for (int64_t i = 0; i < table.size(); ++i) + write(table[i]); +} + +File* File::map(int threads, std::function& f) { + File* output_file = new File(schema_, "", Flags::TEMP); + auto writer = [output_file](TextBuffer* buf) { output_file->out_file_->write(buf->data(), buf->size()); }; + ReorderQueue queue(0, writer); + + function callback = [&f, &queue](int64_t chunk, const Table& t) { + TextBuffer* out = new TextBuffer; + for (int64_t i = 0; i < t.size(); ++i) + f(t[i]).write(*out); + queue.push(chunk, out); + }; + + read(threads, callback); + + return output_file; +} + +File* File::sort(int column, int threads) { + static const int64_t READ_SIZE = 100000; + file_->rewind(); + Table t(schema_); + vector files; + do { + t = read(READ_SIZE, threads); + if (t.size() == 0) + break; + files.push_back(new File(schema_, "", Flags::TEMP)); + files.back()->write(t.sorted(column, threads)); + files.back()->out_file_->rewind(); + files.back()->file_->rewind(); + } while (t.size() == READ_SIZE); + File* out = merge(files.begin(), files.end(), column); + for (File* f : files) + delete f; + return out; +} + +}} \ No newline at end of file diff --git a/src/util/tsv/file.h b/src/util/tsv/file.h new file mode 100644 index 000000000..f39a9bafe --- /dev/null +++ b/src/util/tsv/file.h @@ -0,0 +1,138 @@ +#pragma once +#include +#include +#include +#include +#include +#include "../io/text_input_file.h" +#include "table.h" +#include "../enum.h" +#include "helpers.h" +#include "construct.h" + +namespace Util { namespace Tsv { + +enum class Flags { + READ_WRITE = 1, WRITE = 1 << 1, OVERWRITE = 1 << 2, RECORD_ID_COLUMN = 1 << 3, TEMP = 1 << 4 +}; + +DEFINE_ENUM_FLAG_OPERATORS(Flags); + +struct FileColumn { + int file, column; +}; + +struct Config { + Config(): + line_delimiter("\n") + {} + std::string line_delimiter; +}; + +struct File { + + File(const Schema& schema, const char* file_name, Flags flags = Flags(), const Config& config = Config()); + File(const Schema& schema, const std::string& file_name, Flags flags = Flags(), const Config& config = Config()); + void rewind(); + ~File(); + + void read(int threads, std::function& callback); + void read(int threads, std::function& callback); + Table read(int threads); + Table read(int64_t max_records, int threads); + + template + void read(Out out) { + using T = typename Out::container_type::value_type; + using Tok = TokenIterator; + if ((int)schema_.size() != sizeof...(Targs)) + throw std::runtime_error("Template parameters do not match schema."); + rewind(); + while (file_->getline(), !file_->line.empty() || !file_->eof()) { + Tok it(file_->line.cbegin(), file_->line.cend()); + *out++ = Construct().template operator()(it); + } + } + + template + void read(F& f, Out& out) { + /*using T = typename std::result_of::type; + auto f2 = [](std::vector* v) { + for (std::vector::const_iterator i = v->cbegin(); i != v->cend(); ++i) + *out++ = *i; + } + ReorderQueue*, decltype(f2)> queue; + auto f1 = [&queue](int64_t chunk const Table& table) { + std::vector* v = new vector(); + const int64_t n = table.size(); + v->reserve(n); + for (int64_t i = 0; i < n; ++i) + v->push_back(f(table[i])); + queue.push(chunk, v); + };*/ + } + + Schema schema() const { + return schema_; + } + + void write(const Record& record); + void write(const Table& table); + + template + void write_record(Targs... FArgs) { + write_record((int)schema_.size(), FArgs...); + } + + template + void write_record(const std::tuple& tuple) { + TupleDispatch()(*this, tuple, typename gens::type()); + } + + template + void write(It begin, It end, F& f) { + for (It i = begin; i != end; ++i) + write_record(f(*i)); + } + + File* map(int threads, std::function& f); + + File* sort(int column, int threads); + +private: + + void write_record(int i); + + template + void write_record(int i, T value, Targs... FArgs) { + if (i == 0 || (i == 1 && flag_any(flags_, Flags::RECORD_ID_COLUMN))) + throw std::runtime_error("write_record with too many fields."); + write_buf_ << value; + if (i > 1) + write_buf_ << '\t'; + write_record(i - 1, FArgs...); + } + + template + struct TupleDispatch { + template + void operator()(File& f, const std::tuple& tuple, seq) const { + f.write_record((int)f.schema_.size(), std::get(tuple) ...); + } + }; + + const Flags flags_; + const Schema schema_; + const Config config_; + std::unique_ptr out_file_; + std::unique_ptr file_; + TextBuffer write_buf_; + RecordId record_id_; + + friend File* merge(std::vector::iterator begin, std::vector::iterator end, int column); + friend void join(File& file1, File& file2, int column1, int column2, const std::vector& output_fields, File& out); + friend File* join(File& file1, File& file2, int column1, int column2, const std::vector& output_fields); + +}; + +}} \ No newline at end of file diff --git a/src/util/tsv/helpers.h b/src/util/tsv/helpers.h new file mode 100644 index 000000000..4edf10461 --- /dev/null +++ b/src/util/tsv/helpers.h @@ -0,0 +1,12 @@ +#pragma once + +template +struct seq { }; + +template +struct gens : gens { }; + +template +struct gens<0, S...> { + typedef seq type; +}; diff --git a/src/util/tsv/join.cpp b/src/util/tsv/join.cpp new file mode 100644 index 000000000..7911b2d48 --- /dev/null +++ b/src/util/tsv/join.cpp @@ -0,0 +1,49 @@ +#include +#include "file.h" + +using std::array; +using std::runtime_error; + +namespace Util { namespace Tsv { + +template +static Schema schema(It begin, const std::vector& output_fields) { + Schema schema; + for (auto i = output_fields.cbegin(); i != output_fields.cend(); ++i) + schema.push_back(begin[i->file][i->column]); + return schema; +} + +void join(File& file1, File& file2, int column1, int column2, const std::vector& output_fields, File& out) { + if (output_fields.empty()) + throw runtime_error("Join with empty output"); + array files{ &file1, &file2 }; + array cols{ column1, column2 }; + array tables{ file1.schema(), file2.schema() }; + for (int i = 0; i < 2; ++i) + tables[i] = files[i]->read(1, 1); + while (!tables[0].empty() && !tables[1].empty()) { + array keys = { tables[0].front().get(column1), tables[1].front().get(column2) }; + if (keys[0] < keys[1]) + tables[0] = files[0]->read(1, 1); + else if (keys[1] < keys[0]) + tables[1] = files[1]->read(1, 1); + else { + *out.out_file_ << tables[output_fields.front().file].front().get(output_fields.front().column); + for (auto it = output_fields.begin() + 1; it < output_fields.end(); ++it) + *out.out_file_ << '\t' << tables[it->file].front().get(it->column); + *out.out_file_ << '\n'; + for (int i = 0; i < 2; ++i) + tables[i] = files[i]->read(1, 1); + } + } +} + +File* join(File& file1, File& file2, int column1, int column2, const std::vector& output_fields) { + array schemas{ file1.schema_, file2.schema_ }; + File* out = new File(schema(schemas.begin(), output_fields), "", Flags::TEMP); + join(file1, file1, column1, column2, output_fields, *out); + return out; +} + +}} \ No newline at end of file diff --git a/src/util/tsv/merge.cpp b/src/util/tsv/merge.cpp new file mode 100644 index 000000000..525cf8ac9 --- /dev/null +++ b/src/util/tsv/merge.cpp @@ -0,0 +1,34 @@ +#include +#include "file.h" + +using std::priority_queue; +using std::pair; +using std::vector; +using std::greater; + +namespace Util { namespace Tsv { + +File* merge(std::vector::iterator begin, std::vector::iterator end, int column) { + using pair = pair; + File* out = new File((*begin)->schema(), "", Flags::TEMP); + priority_queue, greater> queue; + vector tables; + tables.reserve(end - begin); + for (auto it = begin; it != end; ++it) { + tables.push_back((*it)->read(1, 1)); + if (tables.back().empty()) + continue; + queue.emplace(tables.back().front().get(column), it - begin); + } + while (!queue.empty()) { + const int64_t f = queue.top().second; + out->write(tables[f].front()); + queue.pop(); + tables[f] = begin[f]->read(1, 1); + if (!tables[f].empty()) + queue.emplace(tables[f].front().get(column), f); + } + return out; +} + +}} \ No newline at end of file diff --git a/src/util/tsv/read_tsv.cpp b/src/util/tsv/read_tsv.cpp new file mode 100644 index 000000000..97b736025 --- /dev/null +++ b/src/util/tsv/read_tsv.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include "tsv.h" +#include "../util/io/input_stream_buffer.h" +#include "file.h" + +using std::mutex; +using std::condition_variable; +using std::runtime_error; +using std::string; +using std::function; +using std::queue; +using std::vector; +using std::search; +using std::copy; +using std::unique_lock; +using std::move; +using std::thread; + +namespace Util { namespace Tsv { + +static const int64_t READ_SIZE = 64 * (1 << 20); + +void File::read(int threads, function& callback) { + queue> buffers; + int64_t next_chunk = 0; + bool stop = false; + const size_t consumers = std::max(threads - 1, 1); + mutex mtx; + condition_variable consume_cv, read_cv; + + auto reader = [&]() { + vector buf(READ_SIZE); + int64_t carry_over = 0, total = 0; + for (;;) { + const int64_t n = file_->read(buf.data() + carry_over, READ_SIZE - carry_over); + total += n; + const int64_t d = (n == READ_SIZE - carry_over) + ? search(buf.rbegin(), buf.rend(), config_.line_delimiter.cbegin(), config_.line_delimiter.cend()) - buf.rbegin() + : READ_SIZE - n - carry_over; + if (d == READ_SIZE && n > 0) + throw runtime_error("Buffer size exceeded."); + if (READ_SIZE - d > 0) { + vector new_buf(buf.begin(), buf.begin() + READ_SIZE - d); + { + unique_lock lock(mtx); + read_cv.wait(lock, [&buffers, consumers] { return buffers.size() < consumers; }); + buffers.push(move(new_buf)); + } + } + if (n < READ_SIZE - carry_over) { + stop = true; + consume_cv.notify_all(); + break; + } else + consume_cv.notify_one(); + copy(buf.end() - d, buf.end(), buf.begin()); + carry_over = d; + } + }; + + auto consumer = [&] { + vector buf; + int64_t chunk; + for (;;) { + { + unique_lock lock(mtx); + consume_cv.wait(lock, [&buffers, &stop] { return stop || !buffers.empty(); }); + if (!buffers.empty()) { + buf = move(buffers.front()); + buffers.pop(); + chunk = next_chunk++; + } + else + return; + } + read_cv.notify_one(); + callback(chunk, buf.data(), buf.data() + buf.size()); + } + }; + + vector thread; + thread.emplace_back(reader); + for (size_t i = 0; i < consumers; ++i) + thread.emplace_back(consumer); + for (auto& t : thread) + t.join(); +} + +}} \ No newline at end of file diff --git a/src/util/tsv/record.cpp b/src/util/tsv/record.cpp new file mode 100644 index 000000000..a30b4bdfb --- /dev/null +++ b/src/util/tsv/record.cpp @@ -0,0 +1,92 @@ +#include +#include "record.h" + +using std::advance; +using std::string; +using std::to_string; + +namespace Util { namespace Tsv { + +static int64_t interpret(const char* ptr, int64_t) { + return *reinterpret_cast(ptr); +} + +static int32_t interpret(const char* ptr, int32_t) { + return *reinterpret_cast(ptr); +} + +static string interpret(const char* ptr, const string&) { + return string(ptr + 4, ptr + 4 + *reinterpret_cast(ptr)); +} + +Record::Iterator::Iterator(Schema::const_iterator it, const char* ptr): + it_(it), + ptr_(ptr) +{} + +Record::Iterator& Record::Iterator::operator++() { + switch (*it_) { + case Type::STRING: + ptr_ += 4 + *(int32_t*)ptr_; + break; + case Type::INT64: + ptr_ += 8; + break; + } + ++it_; + return *this; +} + +string Record::Iterator::operator*() const { + switch (*it_) { + case Type::STRING: + return interpret(ptr_, string()); + case Type::INT64: + return to_string(interpret(ptr_, int64_t())); + default: + throw InvalidType(); + } +} + +template +T Record::Iterator::get() const { + return interpret(ptr_, T()); +} + +template int64_t Record::Iterator::get() const; +template string Record::Iterator::get() const; + +template +T Record::get(int i) const { + Iterator it = begin(); + advance(it, i); + return it.get(); +} + +template int64_t Record::get(int i) const; +template string Record::get(int i) const; + +string Record::get(int i) const { + Iterator it = begin(); + advance(it, i); + return *it; +} + +void Record::write(TextBuffer& buf) const { + int n = 0; + for (Record::Iterator i = begin(); i != end(); ++i) { + if (n++ > 0) + buf << '\t'; + switch (i.type()) { + case Type::INT64: + buf << i.get(); + break; + case Type::STRING: + buf << i.get(); + break; + } + } + buf << '\n'; +} + +}} \ No newline at end of file diff --git a/src/util/tsv/record.h b/src/util/tsv/record.h new file mode 100644 index 000000000..3658ca5f6 --- /dev/null +++ b/src/util/tsv/record.h @@ -0,0 +1,66 @@ +#pragma once +#include +#include +#include "def.h" +#include "../text_buffer.h" + +namespace Util { namespace Tsv { + +struct Record { + + Record(const Schema& schema, const char* begin, const char* end): + schema_(schema), + buf_(begin), + end_(end) + {} + + template + T get(int i) const; + std::string get(int i) const; + + struct Iterator { + using iterator_category = std::forward_iterator_tag; + using difference_type = ptrdiff_t; + using value_type = const char*; + using pointer = const char**; + using reference = const char*&; + Iterator(Schema::const_iterator it, const char* ptr); + Iterator& operator++(); + std::string operator*() const; + bool operator!=(const Iterator& it) const { + return it_ != it.it_; + } + Type type() const { + return *it_; + } + template + T get() const; + private: + Schema::const_iterator it_; + const char* ptr_; + }; + + Iterator begin() const { + return Iterator(schema_.begin(), buf_); + } + + Iterator end() const { + return Iterator(schema_.end(), nullptr); + } + + int64_t raw_size() const { + return end_ - buf_; + } + + void write(TextBuffer& buf) const; + +private: + + const Schema& schema_; + const char* buf_, *end_; + + friend struct Table; + +}; + +}} \ No newline at end of file diff --git a/src/util/tsv/table.cpp b/src/util/tsv/table.cpp new file mode 100644 index 000000000..fc1f89a03 --- /dev/null +++ b/src/util/tsv/table.cpp @@ -0,0 +1,143 @@ +#define _REENTRANT +#include "../../lib/ips4o/ips4o.hpp" +#include "table.h" +#include "../sequence/sequence.h" +#include "../algo/sort_helper.h" +#include "../algo/transform_iterator.h" + +using std::numeric_limits; +using std::runtime_error; +using std::string; +using std::vector; +using std::pair; +using std::less; +using std::move; + +namespace Util { namespace Tsv { + +Table::Table(const Schema& schema) : + schema_(schema), + limits_({ 0 }) +{ +} + +Table::Table(Table&& table) noexcept: + schema_(table.schema_), + data_(move(table.data_)), + limits_(move(table.limits_)) +{} + +template +Table::Table(const Schema& schema, It begin, It end): + Table(schema) +{ + append(begin, end); +} + +void Table::append(const Table& table) { + if (schema_ != table.schema_) + throw SchemaMismatch(); + data_.insert(data_.end(), table.data_.begin(), table.data_.end()); + const int64_t d = limits_.back(); + limits_.reserve(limits_.size() + table.limits_.size() - 1); + for (auto it = table.limits_.cbegin() + 1; it != table.limits_.cend(); ++it) + limits_.push_back(*it + d); +} + +template +void Table::append(It begin, It end) { + LineIterator it(begin, end); + string line; + while (it.good()) { + line = *it; + push_back(line.cbegin(), line.cend(), -1); + ++it; + } +} + +template void Table::append>(const char*, const char*); +//template Table::Table>(const Schema&, const char*, const char*); + +Table& Table::operator=(Table&& table) noexcept { + schema_ = table.schema_; + limits_ = move(table.limits_); + data_ = move(table.data_); + return *this; +} + +void Table::push_back(const Record& record) { + limits_.push_back(limits_.back() + record.raw_size()); + data_.insert(data_.end(), record.buf_, record.end_); +} + +template +void Table::push_back(It begin, It end, RecordId record_id) { + Tok tok(begin, end); + Schema::const_iterator i = schema_.cbegin(); + limits_.push_back(limits_.back()); + if (record_id >= 0) { + ++i; + push_int64(record_id); + } + while (tok.good() && i < schema_.cend()) { + switch (*i) { + case Type::STRING: + push_string(*tok); + break; + case Type::INT64: + push_int64(*tok); + break; + default: + throw runtime_error("Invalid type in schema"); + } + ++tok; + ++i; + } + if (i < schema_.cend()) + throw runtime_error("Missing fields in input line"); +} + +template void Table::push_back>(string::const_iterator, string::const_iterator, RecordId); +template void Table::push_back(const char*, const char*, RecordId); + +void Table::push_string(const std::string& s) { + push_int32((int32_t)s.length()); + data_.insert(data_.end(), s.cbegin(), s.cend()); + limits_.back() += s.length(); +} + +void Table::push_int32(int32_t x) { + data_.insert(data_.end(), reinterpret_cast(&x), reinterpret_cast(&x) + 4); + limits_.back() += 4; +} + +void Table::push_int64(int64_t x) { + data_.insert(data_.end(), reinterpret_cast(&x), reinterpret_cast(&x) + 8); + limits_.back() += 8; +} + +void Table::push_int64(const std::string& s) { + push_int64(convert_string(s)); +} + +void Table::write(TextBuffer& buf) const { + for (int64_t i = 0; i < size(); ++i) + (*this)[i].write(buf); +} + +Table Table::sorted(int col, int threads) { + if ((size_t)col >= schema_.size() || schema_[col] != Type::INT64) + throw runtime_error("Invalid sort"); + vector> v; + v.reserve(size()); + for (int64_t i = 0; i < size(); ++i) + v.emplace_back(this->operator[](i).get(col), i); + ips4o::parallel::sort(v.begin(), v.end(), less<>(), threads); + return shuffle(transform(v.begin(), Second()), transform(v.end(), Second())); +} + +void Table::sort(int col, int threads) { + *this = sorted(col, threads); +} + +}} \ No newline at end of file diff --git a/src/util/tsv/table.h b/src/util/tsv/table.h new file mode 100644 index 000000000..f45c6b1a8 --- /dev/null +++ b/src/util/tsv/table.h @@ -0,0 +1,71 @@ +#pragma once +#include "tsv.h" +#include "def.h" +#include "record.h" + +namespace Util { namespace Tsv { + +struct Table { + + Table(const Schema& schema); + template> + Table(const Schema& schema, It begin, It end); + Table(Table&& table) noexcept; + Table& operator=(Table&& table) noexcept; + + const Schema& schema() const { + return schema_; + } + + int64_t size() const { + return limits_.size() - 1; + } + + bool empty() const { + return size() == 0; + } + + Record operator[](int64_t i) const { + return Record(schema_, data_.data() + limits_[i], data_.data() + limits_[i + 1]); + } + + Record front() const { + return Record(schema_, data_.data(), data_.data() + limits_[1]); + } + + template> + void push_back(It begin, It end, RecordId record_id = -1); + void push_back(const Record& record); + void append(const Table& table); + template> + void append(It begin, It end); + void write(TextBuffer& buf) const; + void sort(int col, int threads); + Table sorted(int col, int threads); + + template + Table shuffle(It begin, It end) { + Table t(schema_); + t.data_.reserve(data_.size()); + t.limits_.reserve(limits_.size()); + for (It i = begin; i != end; ++i) + t.push_back(operator[](*i)); + return t; + } + +private: + + void push_string(const std::string& s); + void push_int32(int32_t x); + void push_int64(int64_t x); + void push_int64(const std::string& s); + + Schema schema_; + std::vector data_; + std::vector limits_; + + friend struct File; + +}; + +}} \ No newline at end of file diff --git a/src/util/string/tsv.cpp b/src/util/tsv/tsv.cpp similarity index 59% rename from src/util/string/tsv.cpp rename to src/util/tsv/tsv.cpp index c076f1303..a2be43c68 100644 --- a/src/util/string/tsv.cpp +++ b/src/util/tsv/tsv.cpp @@ -1,5 +1,11 @@ #include "tsv.h" -#include "tokenizer.h" +#include "../string/tokenizer.h" +#include "../io/text_input_file.h" + +using std::vector; +using std::string; +using std::numeric_limits; +using std::runtime_error; namespace Util { namespace Tsv { @@ -68,4 +74,30 @@ std::vector extract_column(const std::string& buf, const size_t i) return out; } +int64_t count_lines(const std::string& file_name) { + TextInputFile f(file_name); + int64_t n = 0; + while (f.getline(), !f.line.empty() || !f.eof()) + ++n; + f.close(); + return n; +} + +template<> +int64_t convert_string(const char* s) { + char* end; + long long i = strtoll(s, &end, 10); + if ((i == 0 && strcmp(s, "0") != 0) || i == LLONG_MAX || i == LLONG_MIN || *end != '\0' || i < numeric_limits::min() || i > numeric_limits::max()) + throw runtime_error(string("Error converting integer value: ") + s); + return i; +} + +template<> +int32_t convert_string(const char* s) { + const int64_t i = convert_string(s); + if(i < (int64_t)INT32_MIN || i > (int64_t)INT32_MAX) + throw runtime_error(string("Error converting integer value: ") + s); + return (int32_t)i; +} + }} \ No newline at end of file diff --git a/src/util/tsv/tsv.h b/src/util/tsv/tsv.h new file mode 100644 index 000000000..106bf21da --- /dev/null +++ b/src/util/tsv/tsv.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include "../io/text_input_file.h" + +namespace Util { namespace Tsv { + +std::string fetch_block(TextInputFile& f, std::string& buf); +std::string column(const std::string& line, const size_t i); +std::string columns(const std::string& line, const size_t begin, const size_t end); +size_t column_count(const std::string& line); +std::vector extract_column(const std::string& buf, const size_t i); +int64_t count_lines(const std::string& file_name); +template +T convert_string(const char* s); +template +T convert_string(const std::string& s) { + return convert_string(s.c_str()); +} + +template +struct TokenIterator { + TokenIterator(It begin, It end): + ptr_(begin), + end_(end) + {} + bool good() const { + return ptr_ < end_; + } + std::string operator*() const { + return std::string(ptr_, std::find(ptr_, end_, delimiter)); + } + TokenIterator& operator++() { + ptr_ = std::find(ptr_, end_, delimiter); +#ifndef NDEBUG + if (ptr_ < end_) +#endif + ++ptr_; + return *this; + } + It ptr() const { + return ptr_; + } +private: + It ptr_, end_; +}; + +using LineIterator = TokenIterator; + +template +struct FastaIterator { + FastaIterator(It begin, It end): + ptr_(begin), + end_(end) + {} +private: + It ptr_, end_; +}; + +}} \ No newline at end of file diff --git a/src/util/util.cpp b/src/util/util.cpp index 927d83b98..6604e2113 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -28,10 +28,10 @@ along with this program. If not, see . using namespace std; -Message_stream message_stream; -Message_stream verbose_stream (false); -Message_stream log_stream (false); -std::mutex Message_stream::mtx; +MessageStream message_stream; +MessageStream verbose_stream (false); +MessageStream log_stream (false); +std::mutex MessageStream::mtx; std::map Profiler::times; #ifndef _MSC_VER @@ -83,7 +83,7 @@ string join(const char *c, const vector &v) { return s; } -Message_stream& Message_stream::operator<<(std::ostream& (*_Pfn)(std::ostream&)) +MessageStream& MessageStream::operator<<(std::ostream& (*_Pfn)(std::ostream&)) { if (to_cout_) ((*_Pfn)(std::cerr)); @@ -97,7 +97,7 @@ Message_stream& Message_stream::operator<<(std::ostream& (*_Pfn)(std::ostream&)) return *this; } -Message_stream::Message_stream(bool to_cout, bool to_file) : +MessageStream::MessageStream(bool to_cout, bool to_file) : out_stream_(&std::cerr), to_cout_(to_cout), to_file_(to_file) @@ -115,4 +115,67 @@ void exit_with_error(const std::exception& e) { std::cerr << "Error: " << e.what() << endl; log_stream << "Error: " << e.what() << endl; exit(EXIT_FAILURE); +} + +std::vector tokenize(const char* str, const char* delimiters) +{ + std::vector out; + std::string token; + while (*str != 0) { + while (*str != 0 && strchr(delimiters, *str)) + ++str; + token.clear(); + while (*str != 0 && strchr(delimiters, *str) == nullptr) + token += *(str++); + if (token.length() > 0) + out.push_back(token); + } + if (out.size() == 0) + out.push_back(std::string()); + return out; +} + +std::set parse_csv(const std::string& s) +{ + std::set r; + std::vector t(tokenize(s.c_str(), ",")); + for (std::vector::const_iterator i = t.begin(); i != t.end(); ++i) + if (!i->empty()) r.insert(atoi(i->c_str())); + return r; +} + +std::string to_upper_case(const std::string& s) +{ + std::string r; + for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) + r.push_back(toupper(*i)); + return r; +} + +std::string to_lower_case(const std::string& s) +{ + std::string r; + for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) + r.push_back(tolower(*i)); + return r; +} + +std::string print_char(char c) +{ + char buf[16]; + if (c < 32) + sprintf(buf, "ASCII %u", (unsigned)c); + else + sprintf(buf, "%c", c); + return std::string(buf); +} + +std::string hex_print(const char* x, int len) { + std::string out; + char d[3]; + for (int i = 0; i < len; i++) { + sprintf(d, "%02x", (unsigned char)x[i]); + out += d; + } + return out; } \ No newline at end of file diff --git a/src/util/util.h b/src/util/util.h index 308ecd929..c295fed5f 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -1,6 +1,6 @@ /**** DIAMOND protein aligner -Copyright (C) 2013-2021 Max Planck Society for the Advancement of Science e.V. +Copyright (C) 2013-2022 Max Planck Society for the Advancement of Science e.V. Benjamin Buchfink Eberhard Karls Universitaet Tuebingen @@ -21,110 +21,31 @@ along with this program. If not, see . ****/ #pragma once +#include #include #include -#include -#include -#include #include #include -#include -#include #include -#include #include -#include -#include "simd.h" -#include "../basic/const.h" -#include "text_buffer.h" -#include "algo/partition.h" - -template -inline _t div_up(_t x, _t m) -{ return (x + (m-1)) / m; } - -template -inline _t round_up(_t x, _t m) -{ return div_up(x, m) * m; } +#include -inline std::vector tokenize(const char *str, const char *delimiters) +template +inline T div_up(T x, T m) { - std::vector out; - std::string token; - while(*str != 0) { - while(*str != 0 && strchr(delimiters, *str)) - ++str; - token.clear(); - while(*str != 0 && strchr(delimiters, *str) == nullptr) - token += *(str++); - if(token.length() > 0) - out.push_back(token); - } - if(out.size() == 0) - out.push_back(std::string ()); - return out; + return (x + (m - 1)) / m; } -template -struct Pair -{ - Pair(): - first (), - second () - { } - Pair(const _t1 &first, const _t2 &second): - first (first), - second (second) - { } - bool operator<(const Pair &rhs) const - { - return first < rhs.first; - } - _t1 first; - _t2 second; -}; - -inline size_t print_str(char* buf, const char *s, size_t n) +template +inline T round_up(T x, T m) { - memcpy(buf, s, n); - *(buf+n) = 0; - return n; + return div_up(x, m) * m; } -inline size_t print_str(char *buf, const char *s, const char *delimiters) -{ return print_str(buf, s, find_first_of(s, delimiters)); } - -template -struct Static_matrix -{ - _t* operator[](size_t i) - { return data_[i]; } -private: - _t data_[d1][d2]; -}; - +std::vector tokenize(const char* str, const char* delimiters); extern const char dir_separator; std::string extract_dir(const std::string &s); -inline std::ostream& indent(std::ostream &str, unsigned n) -{ - for (unsigned i = 0; i < n; ++i) - str << ' '; - return str; -} - -inline int abs_diff(unsigned x, unsigned y) -{ - return abs((int)x - int(y)); -} - -template -inline void assign_ptr(_t& dst, _t *src) -{ - dst = *src; - delete src; -} - struct Sd { Sd(): @@ -152,177 +73,78 @@ struct Sd double A, Q, k; }; -inline std::string to_upper_case(const std::string &s) -{ - std::string r; - for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) - r.push_back(toupper(*i)); - return r; -} +std::string to_upper_case(const std::string& s); +std::string to_lower_case(const std::string& s); -inline std::string to_lower_case(const std::string &s) -{ - std::string r; - for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) - r.push_back(tolower(*i)); - return r; -} - -template +template struct Matrix { - void init(int rows, int cols, bool reset = false) - { - cols_ = cols; - if (!reset) { - data_.clear(); - data_.resize(rows*cols); - } - else { - data_.clear(); - data_.insert(data_.begin(), rows*cols, _t()); - } - } - _t* operator[](int i) + Matrix(int64_t rows, int64_t cols) : + cols_(cols), + data_(rows* cols, 0) + {} + T* operator[](int64_t i) { - return &data_[i*cols_]; + return &data_[i * cols_]; } private: - int cols_; - std::vector<_t> data_; + int64_t cols_; + std::vector data_; }; template inline int round_down(int x) { - return (x / n)*n; + return (x / n) * n; } template inline int round_up(int x) { - return ((x + n - 1) / n)*n; + return ((x + n - 1) / n) * n; } -template -bool equal(const _t *ptr, unsigned n) -{ - const _t v = *ptr; - const _t* end = (ptr++) + n; - for (; ptr < end; ++ptr) - if (*ptr != v) - return false; - return true; -} +std::string print_char(char c); -inline std::string print_char(char c) +template +T1 percentage(T2 x, T2 y) { - char buf[16]; - if (c < 32) - sprintf(buf, "ASCII %u", (unsigned)c); - else - sprintf(buf, "%c", c); - return std::string(buf); + return x * (T1)100 / y; } -template -struct Top_list -{ - _t& add(const _t &x) - { - for (int i = 0; i < n; ++i) - if ((int)x >(int)data_[i]) { - if (i < n - 1) - memmove(&data_[i + 1], &data_[i], sizeof(data_)/n*(n - 1 - i)); - data_[i] = x; - return data_[i]; - } - } - const _t& operator[](unsigned i) const - { - return data_[i]; - } - _t& operator[](unsigned i) - { - return data_[i]; - } - void sort() - { - std::sort(&data_[0], &data_[n]); - } -private: - _t data_[n]; -}; - -template -_t1 percentage(_t2 x, _t2 y) -{ - return x * (_t1)100 / y; -} - -template -struct Numeric_vector : public std::vector<_t> -{ - Numeric_vector(size_t n): - std::vector<_t>(n) - {} - Numeric_vector& operator+=(Numeric_vector &x) - { - for (size_t i = 0; i < this->size(); ++i) - this->operator[](i) += x[i]; - return *this; - } - Numeric_vector& operator/=(double x) - { - for (size_t i = 0; i < this->size(); ++i) - this->operator[](i) /= x; - return *this; - } - friend std::ostream& operator<<(std::ostream &s, const Numeric_vector &x) - { - for (size_t i = 0; i < x.size(); ++i) - s << x[i] << std::endl; - return s; - } -}; +void print_binary(uint64_t x); -template -inline unsigned get_distribution(const double *p, std::minstd_rand0 &random_engine) +template +inline typename std::enable_if::value, T1>::type safe_cast(T2 x) { - const double x = std::uniform_real_distribution(0.0, 1.0)(random_engine); - double s = 0; - for (unsigned i = 0; i < n; ++i) { - s += p[i]; - if (x < s) - return i; - } - return n - 1; + if (x > (T2)std::numeric_limits::max() || x < (T2)std::numeric_limits::min()) + throw std::runtime_error("Integer value out of bounds."); + return (T1)x; } -void print_binary(uint64_t x); - -template -inline _t safe_cast(size_t x) +template +inline typename std::enable_if::value, T1>::type safe_cast(T2 x) { - if (x > (size_t)std::numeric_limits<_t>::max()) + if (x > (T2)std::numeric_limits::max()) throw std::runtime_error("Integer value out of bounds."); - return (_t)x; + return (T1)x; } -struct Index_iterator + +struct IndexIterator { - Index_iterator(size_t i) : + IndexIterator(size_t i) : i(i) {} size_t operator*() const { return i; } - bool operator!=(const Index_iterator &rhs) const + bool operator!=(const IndexIterator &rhs) const { return i != rhs.i; } - Index_iterator& operator++() + IndexIterator& operator++() { ++i; return *this; @@ -335,50 +157,33 @@ inline double megabytes(size_t x) return (double)x / (1 << 20); } -template -inline _t make_multiple(_t x, _t m) +template +inline T make_multiple(T x, T m) { if (x % m == 0) return x; - _t d = m - x % m; - if (std::numeric_limits<_t>::max() - d < x) + T d = m - x % m; + if (std::numeric_limits::max() - d < x) return x; return x + d; } -inline std::string hex_print(const char *x, int len) { - std::string out; - char d[3]; - for (int i = 0; i < len; i++) { - sprintf(d, "%02x", (unsigned char)x[i]); - out += d; - } - return out; -} - -inline std::set parse_csv(const std::string &s) -{ - std::set r; - std::vector t(tokenize(s.c_str(), ",")); - for (std::vector::const_iterator i = t.begin(); i != t.end(); ++i) - if(!i->empty()) r.insert(atoi(i->c_str())); - return r; -} - +std::string hex_print(const char* x, int len); +std::set parse_csv(const std::string& s); std::string join(const char *c, const std::vector &v); -template -auto apply(const std::vector<_t> &v, _f f) -> std::vector::type> { - std::vector::type> r; +template +auto apply(const std::vector &v, F f) -> std::vector::type> { + std::vector::type> r; r.reserve(v.size()); for (const auto &i : v) r.push_back(f(i)); return r; } -template -std::vector> combine(const std::vector<_t1> &v1, const std::vector<_t2> &v2) { - std::vector> r; +template +std::vector> combine(const std::vector &v1, const std::vector &v2) { + std::vector> r; r.reserve(v1.size()); for (size_t i = 0; i < v1.size(); ++i) r.emplace_back(v1[i], v2[i]); @@ -410,7 +215,8 @@ struct AsyncKeyMerger { template struct KeyMergeIterator { - typedef typename std::result_of::type KeyType; + using value_type = typename std::iterator_traits::value_type; + typedef typename std::result_of::type KeyType; KeyMergeIterator(const It& begin, const It& end, const Key& key) : end_(end), begin_(begin),