3: Local Sequence Alignment: Smith-Waterman Algorithm
Chapter 3: Local Sequence Alignment: Smith-Waterman Algorithm
While global alignment forces entire sequences to align from end to end, biology frequently presents conserved domains or active site motifs embedded within highly divergent non-homologous flanking sequences. Published in 1981 by Temple Smith and Michael Waterman, the Smith-Waterman algorithm identifies the optimal local region of similarity between two sequences.
The defining innovation of Smith-Waterman is the inclusion of zero in the recurrence relation:
H(i, j) = max { 0, H(i-1, j-1) + s(S1[i], S2[j]), H(i-1, j) - d, H(i, j-1) - d }
If an alignment prefix incurs a negative cumulative score (indicating divergent non-homologous sequence), the algorithm drops the score to zero, effectively terminating that candidate alignment and allowing a new local alignment to initiate at any subsequent position.
1. Key Differences from Global Alignment
- Initialization: The first row and column are initialized to
0: \(H(i, 0) = 0\) and \(H(0, j) = 0\). Leading terminal gaps are never penalized. - Traceback Initiation: Traceback does not start at the bottom-right corner. Instead, the algorithm scans the entire matrix to locate the cell containing the maximum value \(\max_{i,j} H(i, j)\).
- Traceback Termination: Traceback terminates as soon as a cell with a value of
0is encountered.
2. Python Implementation
def smith_waterman(seq1, seq2, match=3, mismatch=-3, gap=-2):
m, n = len(seq1), len(seq2)
H = [[0] * (n + 1) for _ in range(m + 1)]
max_score = 0
max_pos = (0, 0)
for i in range(1, m + 1):
for j in range(1, n + 1):
score_diag = H[i-1][j-1] + (match if seq1[i-1] == seq2[j-1] else mismatch)
score_up = H[i-1][j] + gap
score_left = H[i][j-1] + gap
H[i][j] = max(0, score_diag, score_up, score_left)
if H[i][j] > max_score:
max_score = H[i][j]
max_pos = (i, j)
# Traceback from maximum position until 0
align1, align2 = [], []
i, j = max_pos
while H[i][j] > 0:
if H[i][j] == H[i-1][j-1] + (match if seq1[i-1] == seq2[j-1] else mismatch):
align1.append(seq1[i-1])
align2.append(seq2[j-1])
i -= 1; j -= 1
elif H[i][j] == H[i-1][j] + gap:
align1.append(seq1[i-1])
align2.append('-')
i -= 1
else:
align1.append('-')
align2.append(seq2[j-1])
j -= 1
return max_score, ''.join(reversed(align1)), ''.join(reversed(align2))
score, a1, a2 = smith_waterman("TGTTACGG", "GGTTGACTA")
print(f"Max Local Score: {score}\nLocal 1: {a1}\nLocal 2: {a2}")Common Pitfalls & Troubleshooting Checklist
- Java Heap Exhaustion (GATK / Picard): If processes terminate with
java.lang.OutOfMemoryError, set your environment variable explicitly:export _JAVA_OPTIONS="-Xmx16g -Djava.io.tmpdir=/tmp". - Index Timestamp Mismatch: If tools complain that a BAM index is missing or out of date, regenerate with
samtools index -@ 4 sample.bam. - Conda Channel Conflicts: Avoid ABI incompatibilities between bioconda and conda-forge by setting strict priority:
conda config --set channel_priority strict. - Chromosome Naming Contamination: Always verify whether reference FASTA headers use
chr1or1; divergent naming causes silent alignment drops.