图51-5:序列比对动态规划实验台使用匹配 +2、不匹配 −1、可调线性Gap罚分。点击“下一格”,按从上到下、从左到右的顺序填表。
-2
序列AACGT
序列BAGT
已计算内部格子0 / 12
橙色:刚计算的格子。绿色:全部完成后的一条最优回溯路径。负分允许出现在全局模式;局部模式不会保留负分。
python
def align(
a: str,
b: str,
mode: str = "global",
match: int = 2,
mismatch: int = -1,
gap: int = -2,
):
"""
双序列动态规划比对。
mode:
"global" -> Needleman-Wunsch型全局比对
"local" -> Smith-Waterman型局部比对
gap为单个空位字符的得分,通常为负数。
使用线性Gap罚分,而非仿射Gap罚分。
"""
if mode not in ("global", "local"):
raise ValueError("mode必须为global或local")
n = len(a)
m = len(b)
dp = [
[0] * (m + 1)
for _ in range(n + 1)
]
parent = [
[None] * (m + 1)
for _ in range(n + 1)
]
if mode == "global":
for i in range(1, n + 1):
dp[i][0] = dp[i - 1][0] + gap
parent[i][0] = "up"
for j in range(1, m + 1):
dp[0][j] = dp[0][j - 1] + gap
parent[0][j] = "left"
best_score = 0
best_position = (0, 0)
for i in range(1, n + 1):
for j in range(1, m + 1):
pair_score = (
match
if a[i - 1] == b[j - 1]
else mismatch
)
diagonal = (
dp[i - 1][j - 1]
+ pair_score
)
up = dp[i - 1][j] + gap
left = dp[i][j - 1] + gap
if mode == "local":
value = 0
direction = None
else:
value = float("-inf")
direction = None
for candidate, candidate_direction in (
(diagonal, "diag"),
(up, "up"),
(left, "left"),
):
if candidate > value:
value = candidate
direction = candidate_direction
dp[i][j] = value
parent[i][j] = direction
if mode == "local" and value > best_score:
best_score = value
best_position = (i, j)
if mode == "global":
best_score = dp[n][m]
best_position = (n, m)
i, j = best_position
end_a, end_b = i, j
aligned_a = []
aligned_b = []
while i > 0 or j > 0:
if mode == "local" and dp[i][j] == 0:
break
direction = parent[i][j]
if direction == "diag":
aligned_a.append(a[i - 1])
aligned_b.append(b[j - 1])
i -= 1
j -= 1
elif direction == "up":
aligned_a.append(a[i - 1])
aligned_b.append("-")
i -= 1
elif direction == "left":
aligned_a.append("-")
aligned_b.append(b[j - 1])
j -= 1
else:
break
aligned_a.reverse()
aligned_b.reverse()
result_a = "".join(aligned_a)
result_b = "".join(aligned_b)
middle = "".join(
"|" if x == y and x != "-" else " "
for x, y in zip(result_a, result_b)
)
return {
"mode": mode,
"score": best_score,
"aligned_a": result_a,
"aligned_b": result_b,
"middle": middle,
"range_a": (i, end_a),
"range_b": (j, end_b),
"matrix": dp,
}
def print_alignment(result):
print("模式:", result["mode"])
print("得分:", result["score"])
print()
print(result["aligned_a"])
print(result["middle"])
print(result["aligned_b"])
print()
print("A区间:", result["range_a"])
print("B区间:", result["range_b"])
python
result = align(
"ACGT",
"AGT",
mode="global",
match=2,
mismatch=-1,
gap=-2,
)
print_alignment(result)
assert result["score"] == 4
assert result["aligned_a"] == "ACGT"
assert result["aligned_b"] == "A-GT"
print("全局比对测试通过")
python
result = align(
"TTACGTAA",
"GGACGTT",
mode="local",
match=2,
mismatch=-1,
gap=-2,
)
print_alignment(result)
assert result["score"] == 8
assert result["aligned_a"] == "ACGT"
assert result["aligned_b"] == "ACGT"
print("局部比对测试通过")
python
for mode in ("global", "local"):
result = align(
"ACGT",
"ACGT",
mode=mode,
)
assert result["score"] == 8
assert result["aligned_a"] == "ACGT"
assert result["aligned_b"] == "ACGT"
print("完全相同序列测试通过")
python
result = align(
"AAAA",
"CCCC",
mode="local",
)
assert result["score"] == 0
assert result["aligned_a"] == ""
assert result["aligned_b"] == ""
print("无正分局部片段测试通过")