# Needleman-Wunsch Algorithm

## Overview of Sequence Alignment in Bioinformatics

Sequence alignment, a fundamental task in bioinformatics, involves the comparison of biological sequences such as DNA, RNA, or proteins to identify similarities and differences. This process is crucial for understanding the evolutionary relationships between different species, annotating genes, and deciphering the functional elements within genetic material. One of the pioneering algorithms that revolutionized the field of sequence alignment is the Needleman-Wunsch algorithm.

### Genesis of Needleman-Wunsch Algorithm

Developed by Saul B. Needleman and Christian D. Wunsch in 1970, the Needleman-Wunsch algorithm addressed the need for a method to globally align two sequences comprehensively. Prior to its inception, local alignment methods dominated the scene, focusing on identifying subsequences with high similarity. However, for a more holistic understanding of sequence relationships, the scientific community required a tool capable of aligning entire sequences.

### Purpose and Significance

The primary purpose of the Needleman-Wunsch algorithm is to determine the optimal alignment of two sequences by maximizing the number of matching characters and strategically introducing gaps to account for insertions or deletions. This global alignment approach ensures that every position in the sequences is considered, providing a thorough analysis of their similarities and differences. The algorithm's significance lies in its application to bioinformatics, where it serves as a cornerstone for various analyses, including database searches, phylogenetic studies, and functional annotation.

### Algorithmic Underpinnings

The Needleman-Wunsch algorithm employs a dynamic programming paradigm, a powerful technique in computer science that involves breaking down a complex problem into smaller overlapping subproblems. In the context of sequence alignment, dynamic programming enables the algorithm to systematically evaluate all possible alignments and determine the optimal one through a step-by-step process.

1. Dynamic Programming Matrix: The Score Matrix

At the heart of the Needleman-Wunsch algorithm is the dynamic programming matrix, often referred to as the "score matrix." This matrix is a two-dimensional table where each cell corresponds to a specific alignment state between two positions in the sequences. The matrix has n+1 rows and m+1 columns, where n is the length of the first sequence, and m is the length of the second sequence.

The matrix is populated iteratively, and each cell's value represents the cumulative score of a particular alignment state. The values are calculated based on the scores of neighboring cells, creating a comprehensive representation of all possible alignments.

2. Scoring System: Match, Mismatch, and Gap Penalties

The scoring system is a critical component of the Needleman-Wunsch algorithm. It involves assigning scores to match, mismatch, and gap penalties, influencing the algorithm's decision-making during the alignment process.

Match Score (S): A positive score assigned when the characters at aligned positions in the sequences match. This score reflects the similarity between characters.

Mismatch Score (D): A penalty score assigned when the characters at aligned positions in the sequences do not match. This score represents dissimilarity.

Gap Penalty (G): A penalty score assigned for introducing a gap in the alignment. Gaps account for insertions or deletions in the sequences.

### Dynamic Programming Matrix

At the core of the Needleman-Wunsch algorithm is the dynamic programming matrix, often referred to as the "score matrix." This matrix is a two-dimensional table where each cell corresponds to a specific alignment state between two positions in the sequences. The matrix is populated iteratively, and the value in each cell is determined based on the values of its neighboring cells. The dynamic programming matrix serves as a roadmap for the algorithm to navigate through the alignment possibilities.

### Scoring System

A critical aspect of the Needleman-Wunsch algorithm is the scoring system, which involves assigning scores to match, mismatch, and gap penalties. These scores influence the algorithm's decision-making process during the alignment. Match scores contribute positively to the alignment score, indicating similarity between characters, while mismatch scores represent dissimilarity. Gap penalties account for the introduction of gaps in the alignment, penalizing for their occurrence. The careful calibration of these scores ensures the algorithm produces biologically meaningful alignments.

### Dynamic Programming Recurrence Relation

The calculation of scores in the dynamic programming matrix relies on a recurrence relation. This relation defines how the score of a cell is determined based on the scores of its adjacent cells. The recurrence relation incorporates the match, mismatch, and gap penalty scores, providing a comprehensive method for evaluating the alignment states. The dynamic programming matrix is constructed by iteratively applying the recurrence relation to fill in the scores for each cell.

### Algorithmic Workflow

1. Initialization: Establishing the Foundation

The algorithm begins by initializing the dynamic programming matrix, a two-dimensional array that serves as the foundation for evaluating alignment possibilities. This phase involves setting up the top row and leftmost column of the matrix with initial scores.

- Top Row Initialization:

The top row of the matrix corresponds to the alignment of the first sequence with gaps in the second sequence. Each cell in this row is determined by adding the score from the cell to its left (representing the alignment with a gap in the second sequence) and the gap penalty.

Matrix[0][j]=Matrix[0][j-1]+G

Here,

G represents the gap penalty. This process continues for each cell in the top row, establishing the cumulative scores for alignments with increasing numbers of gaps in the second sequence.

- Leftmost Column Initialization:

The leftmost column of the matrix corresponds to the alignment of the second sequence with gaps in the first sequence. Each cell in this column is determined by adding the score from the cell above (representing the alignment with a gap in the first sequence) and the gap penalty.

Matrix[i][0]=Matrix[i-1][0]+G

Similar to the top row, this process continues for each cell in the leftmost column, establishing the cumulative scores for alignments with increasing numbers of gaps in the first sequence.

The initialization phase creates a matrix with scores that reflect the cumulative penalties for introducing gaps in the alignment. It sets the stage for the subsequent steps by providing a starting point for evaluating alignment possibilities.

2. Matrix Filling: Systematic Evaluation of Alignment States

With the matrix initialized, the algorithm proceeds to fill in the remaining cells based on the dynamic programming recurrence relation. The recurrence relation involves considering three potential moves at each cell: a match or mismatch (diagonal move), a gap in the first sequence (vertical move), and a gap in the second sequence (horizontal move).

- Dynamic Programming Recurrence Relation:

The general form of the recurrence relation is as follows: (Gap in the second sequence)

Matrix[i][j]=max

Matrix[i-1][j-1]+S (Match or Mismatch),

Matrix[i-1][j]+G (Gap in the first sequence),

Matrix[i][j-1]+G (Gap in the second sequence)

Here,

S represents the match score or mismatch penalty, and

G represents the gap penalty. The algorithm selects the move that results in the highest score, reflecting the optimal alignment state for the current cell.

- Iterative Matrix Filling:

The algorithm iterates through the remaining cells of the matrix, systematically calculating scores based on the recurrence relation. Starting from the top-left corner and moving row by row, each cell's score is determined by considering the three potential moves. The process continues until the bottom-right corner of the matrix is reached.

As the matrix is filled, each cell contains a score representing the cumulative optimality of the alignment state corresponding to that position. The iterative matrix filling ensures a comprehensive evaluation of all possible alignment states.

3. Backtracking: Constructing the Optimal Alignment

Once the dynamic programming matrix is complete, the algorithm performs a backtracking step to construct the optimal alignment. Starting from the bottom-right cell (representing the end of the sequences), the algorithm traces back through the matrix, following the path of highest scores.

- Backtracking Steps:

Diagonal Move (Match or Mismatch): If the score in the current cell comes from the diagonal move, it indicates a match or mismatch. The aligned characters from the sequences are added to the alignment result.

Vertical Move (Gap in the First Sequence): If the score comes from the vertical move, it indicates a gap in the first sequence. The aligned character from the second sequence and a gap symbol are added to the alignment result.

Horizontal Move (Gap in the Second Sequence): If the score comes from the horizontal move, it indicates a gap in the second sequence. The aligned character from the first sequence and a gap symbol are added to the alignment result.

- Building the Alignment:

As the algorithm backtracks through the matrix, the alignment result is constructed step by step. The process continues until the top-left corner of the matrix is reached, representing the beginning of the sequences.

The final alignment consists of matched characters and introduced gaps, reflecting the optimal global alignment between the two sequences.

Significance of the Algorithmic Workflow:

Global Optimization: The Needleman-Wunsch algorithm's workflow ensures global optimization by considering all possible alignment states through the dynamic programming matrix. The initialization, matrix filling, and backtracking steps collectively lead to the identification of the optimal alignment that maximizes the overall score.

Comprehensive Alignment: The algorithmic workflow guarantees a thorough evaluation of alignment possibilities, allowing the algorithm to adapt to the diverse characters encountered in biological sequences. By considering match scores, mismatch penalties, and gap penalties, the algorithm provides a holistic view of sequence relationships.

Applicability to Various Sequences: The systematic nature of the algorithm makes it applicable to sequences of varying lengths and compositions. It is widely used in bioinformatics for comparing DNA, RNA, and protein sequences, providing valuable insights into evolutionary relationships and functional annotations.

### Below is the program in C++

Output:

```Sequence 1: AGTAC
Sequence 2: GAGC

Dynamic Programming Matrix:
0       -1      -2      -3      -4      -5
-1      2       1       0       -1     -2
-2      1       4       3       2
```

Explanation:

1. Include Statements:

• #include <iostream>: Includes the Input/Output Stream Library for input and output operations.
• #include <vector>: Includes the Vector Library for dynamic array implementation.

2. Constants Declaration:

• const int matchScore = 2;: Defines a constant for the match score.
• const int mismatchPenalty = -1;: Defines a constant for the mismatch penalty.
• const int gapPenalty = -1;: Defines a constant for the gap penalty.

3. Matrix Printing Function:

• void printMatrix(const vector<vector<int>> &matrix): Declares a function to print a 2D matrix of integers.
• for (const auto &row : matrix): Iterates through each row of the matrix.
• for (int val : row): Iterates through each element in the row.
• cout << val << "\t";: Prints each matrix element followed by a tab.
• cout << endl;: Moves to the next line after printing each row.

4. Needleman-Wunsch Function Initialization:

• void needlemanWunsch(const string &sequence1, const string &sequence2): Declares the Needleman-Wunsch function that takes two input sequences.
• int m = sequence1.size();: Gets the length of the first sequence.
• int n = sequence2.size();: Gets the length of the second sequence.

5. Matrix Initialization:

• Initializes a 2D vector dpMatrix with dimensions (m + 1) x (n + 1) and initializes all elements to 0.

6. Matrix Initialization with Gap Penalties:

• Initializes the first row with cumulative gap penalties.
• Initializes the first column with cumulative gap penalties.

7. Matrix Filling Loop:

• Nested loops iterate through the dynamic programming matrix, calculating scores based on the Needleman-Wunsch recurrence relation.

8. Print Dynamic Programming Matrix:

• Prints the dynamic programming matrix using the previously defined printMatrix function.

9. Backtracking Loop:

• Backtracks through the dynamic programming matrix to find the optimal alignment.

10. Print Optimal Alignment:

• Prints the optimal alignment found during the backtracking step.

11. Main Function:

• Initializes two example sequences.
• Prints the sequences.
• Calls the needlemanWunsch function with the input sequences.
• Returns 0 to indicate successful program execution.

### Time Complexity Analysis

The time complexity of the Needleman-Wunsch algorithm is primarily influenced by the two major steps in its execution: matrix filling and backtracking.

Matrix Filling:

The nested loops responsible for matrix filling iterate over each cell in the dynamic programming matrix, considering three potential moves (match/mismatch, gap in the first sequence, and gap in the second sequence) at each step. The loops run for m rows and n columns, where m and n are the lengths of the input sequences.

In each iteration, constant-time operations are performed to calculate scores based on the recurrence relation, resulting in an overall time complexity of O(m⋅n) for the matrix filling step.

Backtracking:

The backtracking step involves tracing the optimal alignment path through the dynamic programming matrix. In the worst case, the algorithm may backtrack from the bottom-right corner to the top-left corner, considering m+n steps. Each backtracking step involves constant-time operations, contributing O(m+n) to the overall time complexity.

Therefore, the dominant factor in the time complexity is the matrix filling step, and the Needleman-Wunsch algorithm has a time complexity of O(m⋅n).

### Space Complexity

The space complexity of the Needleman-Wunsch algorithm is primarily determined by the storage requirements for the dynamic programming matrix. Additionally, a constant amount of space is used for variables such as indices and scores.

Dynamic Programming Matrix:

The dynamic programming matrix is a two-dimensional array with dimensions (m+1)×(n+1), where m and n are the lengths of the input sequences. Each cell in the matrix stores an integer value representing the cumulative score for a specific alignment state. Therefore, the space complexity attributed to the dynamic programming matrix is O(m⋅n).

Apart from the matrix, the algorithm uses a constant amount of additional space for variables such as matchScore, mismatchPenalty, gapPenalty, loop indices, and temporary variables. This constant space complexity is O(1).

Overall Space Complexity:

The overall space complexity of the Needleman-Wunsch algorithm is the sum of the space required for the dynamic programming matrix and the constant additional space. Therefore, the algorithm has an overall space complexity of O(m⋅n)+O(1), which simplifies to O(m⋅n). In conclusion, the Needleman-Wunsch algorithm exhibits a quadratic time complexity (O(m⋅n)), primarily driven by the matrix filling step, and a linear space complexity (O(m⋅n)) due to the storage requirements of the dynamic programming matrix. This analysis provides insights into the algorithm's scalability and efficiency, making it suitable for comparing sequences of varying lengths in bioinformatics applications.

## Applications in Bioinformatics

The Needleman-Wunsch algorithm finds wide-ranging applications in bioinformatics and computational biology:

Database Searches: In genomics, researchers use the algorithm to compare a query sequence against a database of known sequences, identifying homologous regions and inferring functional similarities.

Phylogenetic Analysis: The algorithm aids in the construction of phylogenetic trees by aligning sequences from different species. This facilitates the study of evolutionary relationships and divergence.

Functional Annotation: Understanding the function of genes and proteins is a crucial aspect of molecular biology. The Needleman-Wunsch algorithm assists in annotating functional elements by revealing conserved regions in sequences.

## Conclusion

The Needleman-Wunsch algorithm stands as a pioneering achievement in the field of bioinformatics, providing a robust and versatile tool for sequence alignment. Its dynamic programming approach, coupled with a carefully calibrated scoring system, allows for the identification of optimal global alignments between biological sequences. The algorithm's impact extends beyond mere alignment; it forms the basis for various analyses that contribute to our understanding of the intricate relationships within the genetic code. As technology advances and the volume of biological data grows, the Needleman-Wunsch algorithm remains a cornerstone in the toolkit of bioinformaticians, playing a pivotal role in unraveling the mysteries encoded in the sequences of life.