侧边栏壁纸
  • 累计撰写 38 篇文章
  • 累计创建 16 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

rdkit get_stereo_tags卡死问题分析

子曰
2026-07-15 / 0 评论 / 0 点赞 / 59 阅读 / 1,140 字 / 正在检测是否收录...

SMILES stereo tags issue

Exported on 7/15/2026 at 11:07:43 GMT+8 from Cursor (3.7.27)


User

C1=C[C@@]2(OC)C=C[C@]1(OC)c1ccc(cc1)[C@H]1C=CC@(C=C1)[C@H]1C=CC@H[C@H]1C=CC@(C=C1)[C@H]1C=C[C@@H]2C=C1
在这个smiles使用 get_stereo_tags 会无限循环,卡死,请查看原因


Cursor

正在查找 get_stereo_tags 的实现,并分析该 SMILES 导致无限循环的原因。

get_stereo_tags 调用了 assignStereochemistryCIPLabeler::assignCIPLabels,接下来我会在这两处定位可能的无限循环。

接下来我会用该 SMILES 复现问题,并确认是否由 CIPLabeler 的默认无限迭代引起。

原因分析

问题不在 SMILES 解析,而在 get_stereo_tags() 内部调用的 CIP 立体标签计算

调用链

std::string JSMolBase::get_stereo_tags() {
  bj::object doc;

  bool cleanIt = true;
  bool force = true;
  bool flagPossibleStereocenters = true;
  MolOps::assignStereochemistry(get(), cleanIt, force,
                                flagPossibleStereocenters);
  CIPLabeler::assignCIPLabels(get());
  1. AssignStereochemistry — 很快(约 0.1 ms)
  2. CIPLabeler::assignCIPLabels(get())卡死在这里

根本原因:高对称分子 + 无迭代上限

assignCIPLabels 的默认参数是 maxRecursiveIterations = 0,表示不设上限

  if (maxRecursiveIterations != 0) {
    remainingCallCount = maxRecursiveIterations;
  } else {
    remainingCallCount = UINT_MAX;  // really big - will never be hit
  }

CIP 文档也说明了这一点——高度对称分子会导致“伪无限”计算(例如十二面烷 dodecahedrane):

/*
  Some very symmetrical mols can cause pseudo infinite processing
  (e.g. dodecahedrane)
  To avoid this a maxinum number of iterations can be set by the caller as a
  parameter to assignCIPLabels.
  If that maximum value is exceeded, the following error is thrown
*/

该分子的特征

属性
原子数 44
手性中心 10 个(四面体 CW/CCW)
结构 多环稠合,含多个重复的 [C@](OC)(C=C) 手性单元

实测 CIP 计算耗时(同一 SMILES):

maxRecursiveIterations 结果 耗时
100,000 失败(超出迭代上限) 0.03 s
10,000,000 失败 2.4 s
100,000,000 失败 24.5 s
200,000,000 失败 48.9 s
500,000,000 成功 ~79 s
0(无上限,即 get_stereo_tags 的行为) 成功 ~79 s

结论:该分子 CIP 排序大约需要 2–5 亿次递归比较。不是死循环,但在浏览器/MinimalLib 里会长时间无响应,看起来像“卡死”。

成功时的标签示例:

{2: 's', 7: 's', 16: 'r', 19: 'r', 24: 's', 27: 's', 30: 'r', 33: 'r', 38: 's', 41: 's'}

为何 get_stereo_tags 特别容易出问题

  1. 未传 maxRecursiveIterations,走无上限路径
  2. 未捕获 MaxIterationsExceeded,超时也无法优雅降级
  3. 10 个手性中心相互依赖labelAux 交叉引用),比单中心计算量大得多
  4. 在 JS/WASM 环境中,79 秒可能更长,且无进度反馈

建议修复方向

get_stereo_tags() 中为 CIP 计算设置合理上限,并捕获异常,对未解析中心返回 "?"

// 建议修改 minilib.cpp
try {
  CIPLabeler::assignCIPLabels(get(), 1250000);  // 文档建议的典型上限
} catch (const CIPLabeler::MaxIterationsExceeded &) {
  // 部分中心可能无 CIP 标签,后续逻辑已有 "?" 处理
}

或在 MinimalLib API 中增加可选的 maxRecursiveIterations 参数,让调用方自行控制。


总结:该 SMILES 是高度对称的多环手性分子,CIP 算法需要约 2–5 亿次递归比较(约 1–2 分钟)。get_stereo_tags() 调用 assignCIPLabels 时未设迭代上限,在 UI 中表现为长时间无响应。如需,我可以直接改 minilib.cpp 并补测试。


User

<system_notification>
The following task has finished. If you were already aware, ignore this notification and do not restate prior responses.

kind: shell status: aborted task_id: 104422 title: Test default CIP with 30s timeout output_path: /root/.cursor/projects/data-rdkit/terminals/104422.txt Briefly inform the user about the task result and perform any follow-up actions (if needed). If there's no follow-ups needed, don't explicitly say that.

Cursor

结论

该 SMILES 不是真正的死循环,而是 get_stereo_tags() 内部调用 CIPLabeler::assignCIPLabels()未设置迭代上限(默认 maxRecursiveIterations=0,即无限制)。

这个分子有 10 个手性中心、结构高度对称,CIP 排序大约需要 2–5 亿次递归比较。实测约 79 秒才能完成;在浏览器/MinimalLib 里长时间无响应,看起来像卡死。

卡住的位置AssignStereochemistry 很快(<1 ms),瓶颈在 assignCIPLabels

可选修复:在 minilib.cppget_stereo_tags() 中为 CIP 传入合理的 maxRecursiveIterations,并捕获 MaxIterationsExceeded,对未解析中心返回 "?"。需要的话我可以直接改代码。

0

评论区