1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
|
# Pairwise Alignment {#tutorial_pairwise_alignment}
<b>Learning Objective:</b> <br/>
In this tutorial you will learn how to compute pairwise sequence alignments with SeqAn.
This tutorial is a walkthrough with links to the API documentation and is also meant as a source for copy-and-paste code.
\tutorial_head{Intermediate, 60-90 min, \ref setup \ref tutorial_alphabets \ref tutorial_ranges, }
[TOC]
---
# Introduction
Aligning biological sequences is a very prominent component in many bioinformatics applications and pipelines.
Well known genomic applications that use pairwise sequence alignments are read mapping, genome assembly, variant
detection, multiple sequence alignment as well as protein search.
The goal of the pairwise alignment is to obtain an optimal transcript that describes how two DNA sequences are related
to each other by means of substitutions, insertions, or deletions. The computed transcript describes then the operations
necessary to translate the one sequence into the other, as can be seen in the following picture.
\image html align_transcript.png width=800px
The alignment problem is solved with a dynamic programming (DP) algorithm which runs in \f$ (\mathcal{O}(n^2))\f$ time
and space. Besides the global alignment approach many more variations of this DP based algorithm have been developed
over time. SeqAn unified all of these approaches into a single DP core implementation which can be extended easily and
thus, with all possible configurations, is a very versatile and powerful tool to compute many desired alignment variants.
# Computing pairwise alignments
Let us first have look at an example of computing a global alignment in SeqAn.
\includelineno doc/tutorial/pairwise_alignment/pairwise_alignment_first_global.cpp
In the above example we want to compute the similarity of two seqan3::dna4 sequences using a global alignment.
For this we need to import the dna4 alphabet, the seqan3::nucleotide_scoring_scheme and the seqan3::align_pairwise in
the beginning of our file. We also import the seqan3::debug_stream which allows us to print various types in a nice
formatted manner.
In the beginning of the file we are defining our two DNA sequences `s1` and `s2`.
If you feel puzzled about what the `_dna4` suffix does we recommend to read the \ref tutorial_alphabets and
\ref tutorial_ranges before.
In line 16-17 we configure the alignment job with the most simplistic configuration possible.
In this case it is a global alignment with edit distance.
Later in this tutorial we will give a more detailed description of the \ref alignment_configurations "configuration" and
how it can be used.
The minimum requirement for computing a pairwise alignment is to specify the alignment method
(seqan3::align_cfg::method_local or seqan3::align_cfg::method_global) and the
seqan3::align_cfg::scoring_scheme configuration elements. The first one selects the internal algorithm and the second one
provides the scoring scheme that should be used to score a pair of sequence characters.
Now we are going to call seqan3::align_pairwise. This interface requires two arguments: a tuple or a range of tuples
with exactly two elements and the configuration object. Independent of the number of pairwise alignment jobs submitted
to the algorithm it always returns a range over seqan3::alignment_result. Later we will see how we can use a
continuation interface which calls a user-defined function rather than iterating over the results sequentially.
Finally, we output the score for the computed alignment.
\attention Just calling `pairwise_align` does nothing as it returns a range which is evaluated in a lazy manner.
Only when calling begin or incrementing the iterator over the range the alignment computation is invoked.
\assignment{Assignment 1}
Copy and paste the minimal working example from above into a cpp file in your working directory and compile and run it.
Add the two sequences "ACGTGACTGACT" and "AGGTACGAGCGACACT" to the set and compute all pairwise sequence alignments
of the four sequences and output the scores.
\hint
You can use the seqan3::views::pairwise_combine to generate all pairwise combinations.
\endhint
\endassignment
\solution
\include doc/tutorial/pairwise_alignment/pairwise_alignment_solution_1.cpp
First we create the vector of seqan3::dna4 sequences. We keep the configuration as is and then modify the initial code
to a range-based for loop looping over the alignment results. Since the seqan3::alignment_result is a class template and the
template parameters are determined during the configuration step we use auto as the result type.
The current result is cached inside of the lazy range and we capture the result as `const &` in order to not tamper with
the result values.
\endsolution
Congratulations, you have computed your first pairwise alignment with SeqAn!
As you can see, the interface is really simple, yet the configuration object makes it extremely flexible to conduct
various different alignment calculations. In the following chapter you will learn more about the various configuration
possibilities.
# Alignment configurations
\anchor alignment_configurations
The configuration object is the core of the alignment interface. It allows to easily configure the alignment algorithm
without changing the interface of the pairwise alignment. It uses a seqan3::configuration object to chain different
configuration elements together using the logical or-operator ('|'-operator).
You can find an overview over the available configurations \ref configuration "here".
The configurations for the alignment module are available in:
\snippet doc/tutorial/pairwise_alignment/configurations.cpp include
The configuration elements are all classes that wrap the actual information necessary for the configuration of the
alignment algorithm. Depending on the configuration specification certain features of the algorithm are enabled or
disabled. Moreover, during the initialisation of the algorithm the best implementation is chosen based on the given
configurations. To avoid possible ambiguities with the configurations of other algorithms, the configuration elements
for the alignment are defined in the special namespace seqan3::align_cfg.
## Global and semi-global alignment
The most straightforward algorithm is the global alignment which can be configured using the
seqan3::align_cfg::method_global.
\note The method configuration must be given by the user as it strongly depends on the application context.
It would be wrong for us to assume what the intended default behaviour should be.
The global alignment can be further refined by initialising the seqan3::align_cfg::method_global configuration element
with the free end-gap specifiers. They specify whether gaps at the end of the sequences are penalised.
In SeqAn you can configure this behaviour for every end, namely for leading and trailing gaps of the first and second
sequence. seqan3::align_cfg::method_global is constructed with 4 free end-gap specifiers (one for every end):
- seqan3::align_cfg::free_end_gaps_sequence1_leading - If set to true, aligning leading gaps in first sequence is
not penalised.
- seqan3::align_cfg::free_end_gaps_sequence2_leading - If set to true, aligning leading gaps in second sequence is
not penalised.
- seqan3::align_cfg::free_end_gaps_sequence1_trailing - If set to true, aligning trailing gaps in first sequence is
not penalised.
- seqan3::align_cfg::free_end_gaps_sequence2_trailing - If set to true, aligning trailing gaps in second sequence is
not penalised.
The following code snippet demonstrates the different use cases:
\snippet doc/tutorial/pairwise_alignment/configurations.cpp include_method
\snippet doc/tutorial/pairwise_alignment/configurations.cpp method_global_free_end_gaps
The order of arguments is fixed and must always be as shown in the example.
\assignment{Assignment 2}
Adapt the code from Assignment 1 to compute the semi-global alignment for the case where both ends of the first
sequence can be aligned to gaps without penalising them. Note that in such a semi-global alignment, the first sequence
would be aligned as an infix of the second sequence.
\endassignment
\solution
\include doc/tutorial/pairwise_alignment/pairwise_alignment_solution_2.cpp
To accomplish our goal we initialise the `method_global` option with the free end specifiers
for sequence 1 set to `true`, and those for sequence 2 with `false`.
\endsolution
## Scoring schemes and gap schemes
A scoring scheme can be queried to get the score for substituting two alphabet values using the `score` member function.
Currently, SeqAn supports two scoring schemes: seqan3::nucleotide_scoring_scheme and
seqan3::aminoacid_scoring_scheme. You can import them with the following includes:
\snippet doc/tutorial/pairwise_alignment/configurations.cpp include_scoring_scheme
As the names suggests, you need to use the former when scoring \ref nucleotide "nucleotides" and the latter when working
with \ref aminoacid "aminoacids". You have already used the seqan3::nucleotide_scoring_scheme in the assignments before.
The scoring scheme was default initialised using the
[Hamming Distance](https://en.wikipedia.org/wiki/Hamming_distance). The scoring schemes can also be configured by either
setting a \ref seqan3::scoring_scheme_base::set_simple_scheme "simple scheme" consisting of seqan3::match_score and
seqan3::mismatch_score or by providing a \ref seqan3::scoring_scheme_base::set_custom_matrix "custom matrix".
The amino acid scoring scheme can additionally be \ref seqan3::aminoacid_scoring_scheme::set_similarity_matrix
"initialised" with a predefined substitution matrix that can be accessed via the seqan3::aminoacid_similarity_matrix
enumeration class.
\snippet doc/tutorial/pairwise_alignment/configurations.cpp scoring_scheme
\note You can also provide your own scoring scheme implementation if it models seqan3::scoring_scheme.
Similarly to the scoring scheme, you can use the seqan3::align_cfg::gap_cost_affine to set the gap penalties used for
the alignment computation. The default initialised seqan3::align_cfg::gap_cost_affine sets the score for a gap to `-1`
and for a gap opening to `0`. Note that the gap open score is added to the gap score when a gap is opened within the
alignment computation. Therefore setting the gap open score to `0` disables affine gaps.
You can pass a seqan3::align_cfg::extension_score and a seqan3::align_cfg::open_score object to initialise the scheme
with custom gap penalties. The penalties can be changed later by using the respective member variables
`extension_score` and `open_score`.
\attention SeqAn's alignment algorithm computes the maximal similarity score, thus the match score must be set to a
positive value and the score for mismatch and gaps must be negative in order to maximise over the matching letters.
\snippet doc/tutorial/pairwise_alignment/configurations.cpp include_gap_cost_affine
\snippet doc/tutorial/pairwise_alignment/configurations.cpp gap_cost_affine
To configure the scoring scheme and the gap scheme for the alignment algorithm you need to use the
seqan3::align_cfg::scoring_scheme and the seqan3::align_cfg::gap configurations. The
seqan3::align_cfg::scoring_scheme is mandatory - similarly to the alignment method configuration. It would be
wrong to assume what the default scoring scheme should be. If you do not provide these configurations, the compilation
will fail with a corresponding error message. Not providing the gap scheme is ok. In this case the default initialised
gap scheme will be used for the alignment computation.
\assignment{Assignment 3}
Compute the alignment of the two amino acid sequences listed below using an affine gap scheme with gap costs of `-2` and
gap open costs of `-9`. Use the BLOSUM62 similarity matrix.
- QFSEEILSDIYCWMLQCGQERAV
- AFLPGWQEENKLSKIWMKDCGCLW
\endassignment
\solution
\include doc/tutorial/pairwise_alignment/pairwise_alignment_solution_3.cpp
Only a few parts of our algorithm need to be adapted. First, we use an amino acid scoring scheme and initialise it with
the respective similarity matrix. Second, we initialise the gap scheme to represent the affine gap model as given in
the assignment. Et voilĂ , we have computed a pairwise alignment over aminoacid sequences.
\endsolution
## Alignment result
So far we have only used the score, but obviously in many situations the final alignment is required, e.g. when
mapping reads and the user wishes to write the alignment to the final SAM/BAM file.
In SeqAn you can simply configure what is going to be computed by the alignment algorithm using the
different \ref seqan3_align_cfg_output_configurations "output configurations".
\snippet doc/tutorial/pairwise_alignment/configurations.cpp include_output
\snippet doc/tutorial/pairwise_alignment/configurations.cpp output
Accordingly, the alignment algorithm is configured to use the best implementation to obtain the desired result.
The following table shows the different outcomes that can be configured:
| **Output option** | **Available result** |
| -----------------------------------------------------------------------------------------|------------------------------------------|
| \ref seqan3::align_cfg::output_score "seqan3::align_cfg::output_score" | alignment score |
| \ref seqan3::align_cfg::output_end_position "seqan3::align_cfg::output_end_position" | end positions of the aligned sequences |
| \ref seqan3::align_cfg::output_begin_position "seqan3::align_cfg::output_begin_position" | begin positions of the aligned sequences |
| \ref seqan3::align_cfg::output_alignment "seqan3::align_cfg::output_alignment" | alignment of the two sequences |
| \ref seqan3::align_cfg::output_sequence1_id "seqan3::align_cfg::output_sequence1_id" | id of the first sequence |
| \ref seqan3::align_cfg::output_sequence2_id "seqan3::align_cfg::output_sequence2_id" | id of the second sequence |
The final result is returned as a seqan3::alignment_result object. This object offers special member functions to access
the stored values. If you try to access a value, e.g. the alignment, although you didn't specify
seqan3::align_cfg::output_alignment in the output configuration, a static assertion will be triggered during
compilation.
\note If you don't specify any of the above mentioned output configurations then by default all options are enabled and
will be computed. In order to potentially increase the performance of the alignment algorithm only enable those
options that are needed for your use case.
\assignment{Assignment 4}
Compute the overlap alignment of the following two sequences. Use a linear gap scheme with a gap score of `-4` and
a simple scoring scheme with mismatch `-2` and match `4`.
- TTACGTACGGACTAGCTACAACATTACGGACTAC
- GGACGACATGACGTACGACTTTACGTACGACTAGC
\hint
An alignment is called overlap alignment if it allows free end-gaps at each sequence end. With this configuration
overlaps between two sequences can be computed which is a common use case during the overlap layout consensus assembly.
\endhint
\endassignment
\solution
\include doc/tutorial/pairwise_alignment/pairwise_alignment_solution_4.cpp
\endsolution
## Banded alignment
In many situations it is not necessary to compute the entire alignment matrix but only a part of it. This has
positive impacts on the performance. To limit the computation space the alignment matrix can be bounded by a band.
Thus, only the alignment is computed that fits in this band. Note that this must not be the optimal alignment but in
many cases we can give a rough bound on how similar the sequences will be and therefor use the banded alignment.
To do so, you can configure the alignment using the seqan3::align_cfg::band_fixed_size option. This configuration
element will be initialised with a seqan3::align_cfg::lower_diagonal and seqan3::align_cfg::upper_diagonal parameter.
\snippet doc/tutorial/pairwise_alignment/configurations.cpp include_band
\snippet doc/tutorial/pairwise_alignment/configurations.cpp band
\assignment{Assignment 5}
Use the example from assignment 4 and compute it in a band with lower diagonal set to `-3` and upper diagonal set to `8`.
How does the result change?
\endassignment
\solution
\include doc/tutorial/pairwise_alignment/pairwise_alignment_solution_5.cpp
\endsolution
## Edit distance
A special form of the pairwise sequence alignment is the edit distance. This distance metric counts the number of
edits necessary to transform one sequence into the other. The cost model for the edit distance is fixed. In particular,
the match score is `0` and the scores for a mismatch and a gap is `-1`. Due to the special metric a fast
[bitvector](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.332.9395&rep=rep1&type=pdf) implementation can be
used to compute the edit distance. This happens in SeqAn automatically if the respective configurations are used.
To do so, you need a scoring scheme initialised with Manhattan distance (at the moment only
seqan3::nucleotide_scoring_scheme supports this) and a gap scheme initialised with `-1` for a gap and `0`
for a gap open score and computing a seqan3::global_alignment.
To make the configuration easier, we added a shortcut called seqan3::align_cfg::edit_scheme.
\snippet doc/tutorial/pairwise_alignment/configurations.cpp include_edit
\snippet doc/tutorial/pairwise_alignment/configurations.cpp edit
The `edit_scheme` still has to be combined with an alignment method. When combining it
with the seqan3::align_cfg::method_global configuration element, the edit distance algorithm
can be further refined with free end-gaps (see section `Global and semi-global alignment`).
\attention Only the following free end-gap configurations are supported for the
global alignment configuration with the edit scheme:
- no free end-gaps (all free end-gap specifiers are set to `false`)
- free end-gaps for the first sequence (free end-gaps are set to `true` for the first and
to `false` for the second sequence)
Using any other free end-gap configuration will disable the edit distance algorithm, i.e. the fast bitvector
algorithm, and will fall back to the standard pairwise alignment.
### Refine edit distance
The edit distance can be further refined using the seqan3::align_cfg::min_score configuration to fix an edit score
(a limit of the allowed number of edits). If the respective alignment could not find a solution within the given error
bound, the resulting score is infinity (corresponds to std::numeric_limits::max). Also the alignment and the begin and
end positions of the alignment can be computed using a combination of the seqan3::align_cfg::output_alignment,
seqan3::align_cfg::output_begin_position and seqan3::align_cfg::output_end_position options.
\assignment{Assignment 6}
Compute all pairwise alignments from the assignment 1 (only the scores). Only allow at most 7 errors and
filter all alignments with 6 or less errors.
\hint
You can use the std::views::filter to get only those alignments that fit the requirements.
\endhint
\endassignment
\solution
\include doc/tutorial/pairwise_alignment/pairwise_alignment_solution_6.cpp
\endsolution
## Invalid configurations
Chaining the configurations to build an individual alignment algorithm is a strong advantage of this design. However,
some combinations would result in an invalid alignment configuration. To explicitly prevent this we added some security
details. First, if a combination is invalid (for example by providing the same configuration more than once) a static
assert will inform you about the invalid combination. \ref configuration "Here" you can find a
table depicting the valid configurations. Further, if the seqan3::align_pairwise is called, it checks if the input
data can be used with the given configuration. For example, a static assertion is emitted if the alphabet types of the
sequences together with the provided scoring scheme do not model the concept seqan3::scoring_scheme_for.
Other possible errors are invalid band settings where the initialised band does not intersect with the actual alignment
matrix (the lower diagonal starts beyond the end of the first sequence).
<!-- # Parallel execution -->
|