385

AiZynthFinder:开源逆合成规划工具实战

AiZynthFinder 是阿斯利康开源的逆合成规划工具。这篇给出安装配置、运行代码、结果解析与自定义库存的方法。

AiZynthFinder 是阿斯利康开源的逆合成规划工具,用蒙特卡洛树搜索配合神经网络策略进行路线搜索。它是目前最容易上手、最适合集成进自有流程的开源逆合成工具。

安装与配置

pip install aizynthfinder

# 下载公开数据(模型 + 模板 + 库存)
download_public_data .
# 会生成 config.yml 与相关文件
# config.yml 结构
expansion:
  uspto:
    - uspto_model.onnx        # 单步逆合成模型
    - uspto_templates.csv.gz  # 反应模板库
filter:
  uspto:
    - uspto_filter_model.onnx # 正向反应可行性过滤
stock:
  zinc:
    - zinc_stock.hdf5         # 可购买化合物库

properties:
  time_limit: 120             # 秒
  iteration_limit: 100
  max_transforms: 6           # 最大步数
  cutoff_number: 50           # 每步保留的候选数
  cutoff_cumprob: 0.995

命令行使用

# 单个分子
aizynthcli --config config.yml \
           --smiles "Cc1ccc(cc1)S(=O)(=O)Nc1ccc(cc1)C(=O)O" \
           --output result.json.gz

# 批量(SMILES 文件,每行一个)
aizynthcli --config config.yml \
           --smiles targets.txt \
           --output results.json.gz \
           --nproc 8

Python API

from aizynthfinder.aizynthfinder import AiZynthFinder

finder = AiZynthFinder(configfile="config.yml")
finder.stock.select("zinc")
finder.expansion_policy.select("uspto")
finder.filter_policy.select("uspto")

finder.target_smiles = "Cc1ccc(cc1)S(=O)(=O)Nc1ccc(cc1)C(=O)O"
finder.tree_search()
finder.build_routes()

stats = finder.extract_statistics()
print(f"是否找到完整路线: {stats['is_solved']}")
print(f"最短路线步数:    {stats['number_of_steps']}")
print(f"前体数量:        {stats['number_of_precursors']}")
print(f"搜索用时:        {stats['search_time']:.1f} s")

# 查看路线
for i, route in enumerate(finder.routes[:3]):
    print(f"\n=== 路线 {i} (得分 {route['score']:.3f}) ===")
    # route['reaction_tree'] 是路线的树结构
    # route['image'] 可直接显示路线图(Jupyter 中)

批量筛选可合成性

from aizynthfinder.aizynthfinder import AiZynthFinder
import pandas as pd

def batch_synthesizability(smiles_list, config="config.yml",
                           time_limit=30):
    finder = AiZynthFinder(configfile=config)
    finder.stock.select("zinc")
    finder.expansion_policy.select("uspto")
    finder.filter_policy.select("uspto")
    finder.config.search.time_limit = time_limit

    results = []
    for smi in smiles_list:
        try:
            finder.target_smiles = smi
            finder.tree_search()
            finder.build_routes()
            s = finder.extract_statistics()
            results.append({
                "smiles": smi,
                "solved": s["is_solved"],
                "n_steps": s.get("number_of_steps"),
                "top_score": finder.routes[0]["score"] if finder.routes else None,
            })
        except Exception as e:
            results.append({"smiles": smi, "solved": False,
                            "error": str(e)})
    return pd.DataFrame(results)

# 用于生成模型产出的过滤
df = batch_synthesizability(generated_smiles, time_limit=30)
print(f"可解比例: {df['solved'].mean():.1%}")
print(f"可解分子的平均步数: {df[df['solved']]['n_steps'].mean():.1f}")

# 关键:把 solved 与 n_steps 作为分子设计的筛选维度
# 比单纯的 SA Score(见 348)更贴近真实合成可行性

自定义库存:让结果贴合实际

这是最值得做的定制:默认的 ZINC 库存与你实际能采购或库存的化合物不同。用自家的砌块清单,得到的路线才真正可执行。

from aizynthfinder.context.stock import InMemoryInchiKeyQuery
from rdkit import Chem

# 方法一:从 SMILES 文件建库存
def build_custom_stock(smiles_file, output_hdf5):
    import pandas as pd
    smiles = [line.strip() for line in open(smiles_file) if line.strip()]
    inchi_keys = []
    for smi in smiles:
        m = Chem.MolFromSmiles(smi)
        if m:
            inchi_keys.append(Chem.MolToInchiKey(m))
    df = pd.DataFrame({"inchi_key": inchi_keys})
    df.to_hdf(output_hdf5, key="table", mode="w")

build_custom_stock("our_building_blocks.smi", "custom_stock.hdf5")

# 方法二:在 config.yml 中注册
# stock:
#   inhouse:
#     - custom_stock.hdf5
#   zinc:
#     - zinc_stock.hdf5

# 使用时可同时选多个库存
finder.stock.select(["inhouse", "zinc"])

# 也可以设置库存的优先级或价格信息,
# 让搜索优先使用便宜/现货的原料

结果解析与可视化

import json, gzip

# 从输出文件读取
with gzip.open("results.json.gz") as f:
    data = json.load(f)

for entry in data["data"]:
    print(entry["target"], entry["is_solved"], entry["number_of_steps"])

# Jupyter 中可视化路线
from aizynthfinder.interfaces import AiZynthApp
app = AiZynthApp("config.yml")     # 交互式界面

# 或直接显示路线图
from IPython.display import display
display(finder.routes[0]["image"])

调参建议

参数 影响 建议
time_limit 搜索时长 批量筛选 15~30 s;重点分子 120 s+
max_transforms 最大步数 默认 6;复杂分子可加大但会变慢
cutoff_number 每步保留候选数 50 常用;加大提高覆盖但更慢
filter policy 正向可行性过滤 建议开启,能剔除不合理的切断
stock 可购买库 用自家库存最有价值

局限与注意

  • 「找不到路线」不等于「不能合成」:可能只是模板库覆盖不到,或搜索时间不够。这是最容易被误读的输出。
  • 「找到路线」也不等于「路线可行」:模型没见过失败反应,可能给出行不通的方案。必须由合成化学家评审。
  • USPTO 模板库有偏:来自专利反应,某些反应类型过度代表。
  • 不处理立体化学的选择性:手性构建的可行性需要人工判断。
  • 不给出反应条件:需要配合条件预测(见 389《反应条件与产率预测》)或人的经验。

常见坑与提示

  • 用自家砌块库存替换默认 ZINC,是最值得做的定制;
  • 建议开启 filter policy,能剔除化学上不合理的切断;
  • 「找不到路线」可能只是模板覆盖不足,不等于不能合成
  • 批量筛选用短 time_limit,重点分子单独跑长时间。

延伸资源