跳转到内容

OLC:Overlap-Layout-Consensus

快速概览

OLC 是经典的三阶段组装范式:先找 reads 之间的重叠关系,再构建布局确定相对位置,最后综合多条 read 的信息生成共识序列。

  • Overlap:快速筛选并精确计算 read 之间的重叠
  • Layout:将重叠关系组织成图或路径,推断顺序和方向
  • Consensus:利用冗余覆盖纠正错误,输出稳定序列
  • 长读长时代,OLC 思路因重叠信息更丰富而重新受到重视
所属板块 核心方法

索引、比对、组装与概率模型构成的核心算法主轴。

阅读目标 帮助建立阅读上下文

先判断这页与你当前问题的关系,再决定是否深入展开。

建议前置 先建立相关基础对象与方法直觉

建议先建立相关基础对象与方法直觉,再进入本页。

OLC(Overlap-Layout-Consensus)是一类经典组装思路,它把序列重建问题拆成三个阶段:

  1. Overlap:寻找 reads 之间的重叠关系;
  2. Layout:根据重叠关系构建布局;
  3. Consensus:从布局中求共识序列。

和 de Bruijn graph 相比,OLC 更强调”read 与 read 之间的真实重叠”,而不是先分解成 k-mer 再建图。

组装的核心问题始终是:如何从局部观测恢复整体序列。

在 OLC 框架中,我们假设:

  • 如果两条 read 有足够可靠的重叠,它们就可能来自原始序列上的相邻区域;
  • 把许多这样的重叠关系拼接起来,就有机会恢复更长的序列骨架;
  • 最后再用多条 read 的信息求出更稳健的共识序列。

长读长时代让这套思路重新变得非常重要,因为长读长之间的重叠信息更丰富,更有机会跨过重复区域并提供稳定布局线索。

OLC 组装流程示意图
OLC 组装的三个核心阶段:重叠检测、布局构建与一致性序列生成。

目标是找出哪些 reads 之间存在显著重叠。

这一阶段最直接,但往往也是最贵的,因为潜在 read 对数目很大。要解决的问题包括:

  • 如何快速筛掉不可能重叠的 read 对;
  • 如何区分真实重叠与重复/错误造成的假重叠;
  • 如何给重叠边打分。

得到重叠关系后,可以把 reads 组织成图或路径结构,尝试推断它们在原始序列上的相对顺序和方向。

这一阶段最容易遇到的麻烦是:

  • 重复区域造成歧义;
  • 某些 read 质量差,使布局不稳定;
  • 图中存在多个看似合理的延伸方向。

即使 layout 大体正确,read 本身也可能有错误。因此最后还需要把多条重叠 read 综合起来,得到更可靠的 consensus sequence。

这一步强调的是”用冗余覆盖纠正单条 read 的错误”。

假设有三条 read:

R1: ACGTAC
R2: GTACGA
R3: ACGATT

如果发现:

  • R1R2GTAC 上有高质量重叠;
  • R2R3ACGA 上有高质量重叠;

那么 layout 阶段就可能推断它们沿同一条更长序列排列。最后 consensus 阶段再综合覆盖信息,输出更稳定的结果。

OLC 不是单一算法,而是一整类设计范式。它的思想在很多长读长组装和共识构建流程中都能看到:

  • 先找重叠;
  • 再构布局;
  • 最后做共识纠错。

与 de Bruijn graph 相比,可以粗略理解为:

思路更关注什么常见优势常见挑战
OLCread-read overlap长读长信息利用更直接overlap 搜索昂贵
de Bruijn graphk-mer 图结构短读长场景更高效对错误和重复非常敏感

因此,它们并不是简单的”新旧替代关系”,而更像不同数据条件下的不同建模选择。

给定两条 read r1r_1r2r_2,目标是找到它们之间的最大重叠。这可以形式化为序列对齐问题。

算法 1:后缀-前缀最长公共子串

输入:read r₁, read r₂,最小重叠长度 L_min
输出:最大重叠长度和重叠位置
1. 构造后缀数组 SA₁ 包含 r₁ 的所有后缀
2. 构造前缀数组 PA₂ 包含 r₂ 的所有前缀
3. 对于每个后缀 s ∈ SA₁:
a. 如果 |s| < L_min,跳过
b. 在 PA₂ 中二分搜索与 s 匹配的最长前缀
c. 记录匹配长度
4. 返回最大匹配长度及其位置

时间复杂度O(nlogn)O(n \log n),其中 nn 为 read 长度(使用后缀数组)

空间复杂度O(n)O(n)

为了避免对所有 read 对进行昂贵的对齐计算,先使用 k-mer 进行快速筛选:

算法 2:k-mer 索引筛选

输入:read 集合 R,k-mer 长度 k,最小共享 k-mer 数 t
输出:候选重叠对集合 C
1. 构建倒排索引 I:k-mer → {read ids}
2. 对于每个 read r ∈ R:
a. 提取 r 的所有 k-mer 集合 K_r
b. 对于每个 k-mer k' ∈ K_r:
- 将 r 添加到 I[k']
3. 对于每对 read (r_i, r_j):
a. 计算 |K_{r_i} ∩ K_{r_j}|
b. 如果 |K_{r_i} ∩ K_{r_j}| ≥ t,加入候选集 C
4. 返回 C

时间复杂度O(NL)O(N \cdot L) 构建,O(CL)O(|C| \cdot L) 验证,其中 NN 为 read 数量,LL 为 read 长度

将 reads 作为顶点,重叠关系作为边,构建有向图:

定义:重叠图 G=(V,E)G = (V, E),其中

  • VV:所有 reads
  • E={(ri,rj,w,o)}E = \{(r_i, r_j, w, o)\}rir_irjr_j 有权重为 ww、偏移为 oo 的重叠

算法 3:重叠图构建

输入:候选重叠对集合 C
输出:重叠图 G = (V, E)
1. 初始化空图 G
2. 对于每个候选重叠对(r_i, r_j) ∈ C:
a. 计算精确重叠长度 o 和对齐分数 s
b. 如果 s ≥ 阈值 S_min:
- 添加边(r_i, r_j) 到 E
- 设置边权重 w = s
3. 返回 G

在重叠图中寻找 Hamiltonian 路径或近似路径:

算法 4:贪心路径延伸

输入:重叠图 G = (V, E)
输出:路径集合 P
1. P = ∅
2. 标记所有顶点为未访问
3. 当存在未访问顶点时:
a. 选择未访问且入度最小的顶点 v_start
b. path = [v_start]
c. 标记 v_start 为已访问
d. while 当前顶点 v 有出边:
- 选择权重最大的边(v, u)
- 如果 u 未访问:
* 将 u 加入 path
* 标记 u 为已访问
* v = u
- 否则:break
e. 将 path 加入 P
4. 返回 P

时间复杂度O(V+E)O(|V| + |E|),假设每次选择出边是常数时间

注意:这是启发式算法,不保证最优解。精确的 Hamiltonian 路径问题是 NP-hard。

给定 layout 中的重叠 reads,计算 consensus 序列:

算法 5:基于列投票的 Consensus

输入:对齐后的 reads A = {a₁, a₂, ..., a_m}(每行长度为 n)
输出:consensus 序列 c
1. c = ""(空字符串)
2. 对于列 j = 1 到 n:
a. 统计该列各碱基的计数:count[A, C, G, T, -]
b. 计算各碱基的频率
c. 选择频率最高的碱基 b_max
d. 如果 b_max 的频率 ≥ 阈值 Q_min:
- c += b_max
否则:
- c += 'N'(不确定)
3. 返回 c

时间复杂度O(mn)O(m \cdot n),其中 mm 为 reads 数量,nn 为对齐长度

考虑测序质量分数的改进算法:

算法 6:质量加权 Consensus

输入:对齐后的 reads A,质量分数 Q = {q₁, q₂, ..., q_m}
输出:consensus 序列 c
1. c = ""
2. 对于列 j = 1 到 n:
a. 对于每个碱基 b ∈ {A, C, G, T}:
- score[b] = Σ log₁₀(P(b|q)),其中 P(b|q) 是质量分数 q 下碱基 b 的正确概率
b. 选择 score 最大的碱基 b_max
c. c += b_max
3. 返回 c

质量分数转换:质量分数 qq 通常定义为 q=10log10(Perror)q = -10 \log_{10}(P_{error})

阶段主要算法时间复杂度空间复杂度
Overlapk-mer 筛选 + 动态规划O(NL)O(N \cdot L)O(NL)O(N \cdot L)
Layout图构建 + 贪心路径O(V+E)O(V+E)
Consensus列投票O(mn)O(m \cdot n)O(n)O(n)

其中 NN 为总 read 数量,LL 为平均 read 长度,mm 为局部区域 read 数量,nn 为对齐长度。