Loading...
Searching...
No Matches
debug_matrix.hpp
Go to the documentation of this file.
// SPDX-FileCopyrightText: 2006-2025 Knut Reinert & Freie Universität Berlin
// SPDX-FileCopyrightText: 2016-2025 Knut Reinert & MPI für molekulare Genetik
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
#include <iomanip>
#include <sstream>
#include <seqan3/alignment/matrix/detail/matrix_concept.hpp>
#include <seqan3/alignment/matrix/detail/row_wise_matrix.hpp>
#include <seqan3/alignment/matrix/detail/trace_directions.hpp>
#include <seqan3/alphabet/detail/debug_stream_alphabet.hpp>
#include <seqan3/core/debug_stream/debug_stream_type.hpp>
#include <seqan3/core/debug_stream/optional.hpp>
#include <seqan3/core/debug_stream/range.hpp>
#include <seqan3/core/detail/template_inspection.hpp>
namespace seqan3::detail
{
template <matrix matrix_t, typename first_sequence_t = std::nullopt_t, typename second_sequence_t = std::nullopt_t>
class debug_matrix
{
protected:
static constexpr bool has_first_sequence = !std::is_same_v<std::decay_t<first_sequence_t>, std::nullopt_t>;
static constexpr bool has_second_sequence = !std::is_same_v<std::decay_t<second_sequence_t>, std::nullopt_t>;
using entry_t = typename std::remove_reference_t<matrix_t>::value_type;
static constexpr bool is_traceback_matrix = std::is_same_v<std::decay_t<entry_t>, trace_directions>;
static constexpr bool is_optional_score = is_type_specialisation_of_v<entry_t, std::optional>;
public:
using value_type = std::conditional_t<is_traceback_matrix || is_optional_score, entry_t, std::optional<entry_t>>;
using reference = value_type;
using const_reference = reference;
using size_type = typename std::remove_reference_t<matrix_t>::size_type;
debug_matrix() = default;
debug_matrix(debug_matrix const &) = default;
debug_matrix(debug_matrix &&) = default;
debug_matrix & operator=(debug_matrix const &) = default;
debug_matrix & operator=(debug_matrix &&) = default;
~debug_matrix() = default;
debug_matrix(matrix_t matrix) : debug_matrix(std::forward<matrix_t>(matrix), std::nullopt, std::nullopt)
{}
debug_matrix(matrix_t matrix, first_sequence_t first_sequence, second_sequence_t second_sequence) :
_matrix{std::forward<matrix_t>(matrix)},
_first_sequence{std::forward<first_sequence_t>(first_sequence)},
_second_sequence{std::forward<second_sequence_t>(second_sequence)}
{
if constexpr (has_first_sequence)
{
assert(_matrix.cols() <= _first_sequence.size() + 1u);
}
if constexpr (has_second_sequence)
{
assert(_matrix.rows() <= _second_sequence.size() + 1u);
}
}
size_t rows() const noexcept
{
if (!_transpose)
return _rows.value_or(_matrix.rows());
else
return _cols.value_or(_matrix.cols());
}
size_t cols() const noexcept
{
if (!_transpose)
return _cols.value_or(_matrix.cols());
else
return _rows.value_or(_matrix.rows());
}
first_sequence_t const & first_sequence() const noexcept
{
if (!_transpose)
return _first_sequence;
else
return _second_sequence;
}
second_sequence_t const & second_sequence() const noexcept
{
if (!_transpose)
return _second_sequence;
else
return _first_sequence;
}
const_reference at(matrix_coordinate const & coordinate) const noexcept
{
size_t row = coordinate.row;
size_t col = coordinate.col;
assert(row < rows() && col < cols());
row_index_type const _row{!_transpose ? row : col};
column_index_type const _col{!_transpose ? col : row};
row_index_type const _mask_row{_transpose == _transpose_mask ? row : col};
column_index_type const _mask_col{_transpose == _transpose_mask ? col : row};
if (!_masking_matrix.has_value() || _masking_matrix.value().at({_mask_row, _mask_col}))
{
entry_t const & entry = _matrix.at({_row, _col});
if (!is_traceback_matrix || !_transpose)
return entry;
if constexpr (is_traceback_matrix)
{
trace_directions reverse{};
if ((entry & trace_directions::left) == trace_directions::left)
reverse |= trace_directions::up;
if ((entry & trace_directions::up) == trace_directions::up)
reverse |= trace_directions::left;
if ((entry & trace_directions::diagonal) == trace_directions::diagonal)
reverse |= trace_directions::diagonal;
return reverse;
}
}
if constexpr (is_traceback_matrix)
return trace_directions::none;
else
return std::nullopt;
}
debug_matrix & mask_matrix(row_wise_matrix<bool> masking_matrix) noexcept
{
assert(masking_matrix.rows() == rows());
assert(masking_matrix.cols() == cols());
_transpose_mask = _transpose;
_masking_matrix = std::move(masking_matrix);
return *this;
}
debug_matrix & mask_matrix(std::vector<bool> masking_vector) noexcept
{
return mask_matrix(row_wise_matrix<bool>{number_rows{rows()}, number_cols{cols()}, std::move(masking_vector)});
}
debug_matrix & sub_matrix(size_t const new_rows, size_t const new_cols) noexcept
{
assert(new_rows <= rows());
assert(new_cols <= cols());
if (!_transpose)
{
_rows = new_rows;
_cols = new_cols;
}
else
{
_rows = new_cols;
_cols = new_rows;
}
return *this;
}
debug_matrix & transpose_matrix() noexcept
{
_transpose = !_transpose;
return *this;
}
protected:
struct format_type; // forward declaration
public:
template <typename ostream_t>
void stream_matrix(ostream_t & cout, fmtflags2 const flags) const noexcept
{
format_type const & symbols = (flags & fmtflags2::utf8) == fmtflags2::utf8 ? unicode : csv;
size_t const column_width =
this->column_width.has_value() ? this->column_width.value() : auto_column_width(flags);
auto char_first_sequence = [&]([[maybe_unused]] size_t const i) -> std::string
{
if constexpr (!has_first_sequence)
return " ";
else
return as_string(first_sequence()[i], flags);
};
auto char_second_sequence = [&]([[maybe_unused]] size_t const i) -> std::string
{
if constexpr (!has_second_sequence)
return " ";
else
return as_string(second_sequence()[i], flags);
};
auto print_cell = [&](std::string const & symbol)
{
// deal with unicode chars that mess up std::setw
size_t const length_bytes = symbol.size();
size_t const length = unicode_str_length(symbol);
size_t const offset = length_bytes - length;
cout << std::left << std::setw(column_width + offset) << symbol << symbols.col_sep;
};
auto print_first_cell = [&](std::string const & symbol)
{
cout << symbol << symbols.col_sep;
};
// |_|d|a|t|a|b|a|s|e|
auto print_first_row = [&]
{
print_first_cell(" ");
print_cell(symbols.epsilon);
for (size_t col = 0; col < cols() - 1; ++col)
print_cell(char_first_sequence(col));
cout << "\n";
};
// |-|-|-|-|-|-|-|-|-|
auto print_divider = [&]
{
cout << " " << symbols.row_col_sep;
for (size_t col = 0; col < cols(); ++col)
{
for (size_t i = 0; i < column_width; ++i)
cout << symbols.row_sep;
cout << symbols.row_col_sep;
}
cout << "\n";
};
print_first_row();
for (size_t row = 0; row < rows(); ++row)
{
if (symbols.row_sep[0] != '\0')
print_divider();
// one query letter + one row of scores / traces
if (row == 0)
print_first_cell(symbols.epsilon);
else
print_first_cell(char_second_sequence(row - 1));
for (size_t col = 0; col < cols(); ++col)
print_cell(entry_at({row_index_type{row}, column_index_type{col}}, flags));
cout << "\n";
}
}
size_t auto_column_width(fmtflags2 const flags) const noexcept
{
size_t col_width = 1;
for (size_t row = 0; row < rows(); ++row)
for (size_t col = 0; col < cols(); ++col)
col_width =
std::max(col_width,
unicode_str_length(entry_at({row_index_type{row}, column_index_type{col}}, flags)));
return col_width;
}
protected:
std::string entry_at(matrix_coordinate const coordinate, fmtflags2 flags) const noexcept
{
format_type const & symbols = (flags & fmtflags2::utf8) == fmtflags2::utf8 ? unicode : csv;
value_type const & entry = at(coordinate);
if (!is_traceback_matrix && entry == matrix_inf<value_type>)
return symbols.inf;
return as_string(entry, flags);
}
template <typename value_type>
static std::string as_string(value_type && entry, fmtflags2 const flags) noexcept
{
std::stringstream strstream;
debug_stream_type stream{strstream};
stream << flags << entry;
return strstream.str();
}
static size_t unicode_str_length(std::string const & str) noexcept
{
size_t length = 0u;
for (auto it = str.cbegin(); it < str.cend(); ++it, ++length)
{
uint8_t v = *it;
if ((v & 0b1110'0000) == 0b1100'0000)
++it;
else if ((v & 0b1111'0000) == 0b1110'0000)
it += 2;
else if ((v & 0b1111'1000) == 0b1111'0000)
it += 3;
}
return length;
}
struct format_type
{
char const * epsilon{};
char const * col_sep{};
char const * row_sep{};
char const * row_col_sep{};
char const * inf{};
};
static constexpr format_type csv{" ", ";", "", "", ""};
static constexpr format_type unicode{"ε", "║", "═", "╬", "∞"};
public:
std::optional<size_t> column_width{std::nullopt};
protected:
matrix_t _matrix;
first_sequence_t _first_sequence;
second_sequence_t _second_sequence;
std::optional<size_t> _rows{};
std::optional<size_t> _cols{};
std::optional<row_wise_matrix<bool>> _masking_matrix{};
bool _transpose{};
bool _transpose_mask{};
};
template <matrix matrix_t>
debug_matrix(matrix_t &&) -> debug_matrix<matrix_t>;
template <matrix matrix_t, typename first_sequence_t, typename second_sequence_t>
debug_matrix(matrix_t &&,
first_sequence_t &&,
second_sequence_t &&) -> debug_matrix<matrix_t, first_sequence_t, second_sequence_t>;
} // namespace seqan3::detail
namespace seqan3
{
template <detail::matrix alignment_matrix_t>
struct alignment_matrix_printer<alignment_matrix_t>
{
template <typename stream_t, typename arg_t>
requires detail::is_type_specialisation_of_v<stream_t, debug_stream_type>
constexpr void operator()(stream_t & stream, arg_t && arg) const
{
print_impl(stream.get_underlying_stream(), stream.flags2(), std::forward<arg_t>(arg));
}
template <typename stream_t, typename arg_t>
constexpr void operator()(stream_t & stream, arg_t && arg) const
{
print_impl(stream, fmtflags2::none, std::forward<arg_t>(arg));
}
private:
template <typename stream_t, typename arg_t>
void print_impl(stream_t & stream, fmtflags2 const flags, arg_t && arg) const
{
detail::debug_matrix debug{std::forward<arg_t>(arg)};
debug.stream_matrix(stream, flags);
}
};
} // namespace seqan3
debug_stream_alphabet.hpp
Provides seqan3::debug_stream and related types.
debug_stream_type.hpp
Provides seqan3::debug_stream and related types.
std::forward
T forward(T... args)
seqan3::fmtflags2
fmtflags2
Flags that change the behaviour of the seqan3::debug_stream.
Definition debug_stream_type.hpp:32
seqan3::fmtflags2::utf8
@ utf8
Enables use of non-ASCII UTF8 characters in formatted output.
Definition debug_stream_type.hpp:34
seqan3::fmtflags2::none
@ none
No flag is set.
Definition debug_stream_type.hpp:33
seqan3::field::offset
@ offset
Sequence (seqan3::field::seq) relative start position (0-based), unsigned value.
Definition record.hpp:66
iomanip
std::is_same_v
T is_same_v
std::left
T left(T... args)
matrix_concept.hpp
Provides seqan3::detail::matrix.
std::max
T max(T... args)
seqan3
The main SeqAn3 namespace.
Definition aligned_sequence_concept.hpp:26
optional.hpp
Provides seqan3::debug_stream and related types.
range.hpp
Provides seqan3::debug_stream and related types.
std::reverse
T reverse(T... args)
row_wise_matrix.hpp
Provides seqan3::detail::row_wise_matrix.
std::setw
T setw(T... args)
sstream
std::stringstream::str
T str(T... args)
seqan3::alignment_matrix_printer< alignment_matrix_t >::operator()
constexpr void operator()(stream_t &stream, arg_t &&arg) const
Prints the alignment matrix into the given stream using ascii formatting.
Definition debug_matrix.hpp:512
seqan3::alignment_matrix_printer< alignment_matrix_t >::operator()
constexpr void operator()(stream_t &stream, arg_t &&arg) const
Prints the alignment matrix into the given stream using formatting specified by seqan3::fmtflags2.
Definition debug_matrix.hpp:496
seqan3::alignment_matrix_printer
Definition default_printer.hpp:28
template_inspection.hpp
Provides type traits for working with templates.
trace_directions.hpp
Provides the declaration of seqan3::detail::trace_directions.
Hide me