ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

pySMT UNSAT Core实战:像调试Bug一样解决爱因斯坦50问(含完整代码)

pySMT UNSAT Core实战:像调试Bug一样解决爱因斯坦50问(含完整代码) pySMT UNSAT Core实战像调试Bug一样解决爱因斯坦50问含完整代码【免费下载链接】pysmtpySMT: A library for SMT formulae manipulation and solving项目地址: https://gitcode.com/gh_mirrors/py/pysmtpySMT 是一个 Python 库用于 SMT 公式的构建与求解它的UNSAT Core不可满足核能力能让你像调试 Bug 一样快速定位是哪几条约束互相打架。本文以经典逻辑谜题爱因斯坦50问为例手把手教你用 pySMT 的 UNSAT Core 三步定位冲突约束并附完整代码与踩坑清单。什么是爱因斯坦50问5 栋房子排成一排每栋住着一位不同国籍的人各有不同的宠物、饮品和香烟品牌。根据 15 条线索推理出谁养了鱼这是一个天然的约束满足问题也是官方仓库用来演示 UNSAT Core 调试的示例程序 einstein.py。一、为什么用 UNSAT Core 调试模型SMT 求解器回答可行/不可行时UNSAT Core 会额外告诉你导致不可满足的最小约束子集。它的价值和编译器报错完全一样没有 UNSAT Core有 UNSAT Core求解器只回一句UNSAT500 条约束里大海捞针直接列出肇事的 3~5 条约束按图索骥靠逐条删减、二分排查费时费力一条 API 调用秒级定位核心心智模型把编码的谜题/业务规则当成代码UNSAT Core 就是错误堆栈。二、UNSAT Core 30 秒快速上手1. 安装 pySMT 与求解器pip install pysmt # 安装一个支持 UNSAT Core 的求解器Z3 或 MathSAT 均可 pysmt-install --z3 # 检查 pySMT 可见的求解器 pysmt-install --check如需源码克隆仓库git clone https://gitcode.com/gh_mirrors/py/pysmt2. 最小示例两条矛盾约束from pysmt.shortcuts import Symbol, Not, BOOL, get_unsat_core x Symbol(x, BOOL) core get_unsat_core([x, Not(x)]) # x 与 ¬x 矛盾 print(core) # {x, !x} —— 核心就是这两条get_unsat_core的入口定义见 shortcuts.py传入一组子句返回使它们合取不可满足的约束集合。三、爱因斯坦50问建模步骤1. 把谜题写成 5×5 的布尔符号表每栋房子编号 0~4在每个维度颜色、国籍、宠物、饮品、香烟上各有一个布尔变量例如color(1, green)表示1 号房子是绿色。示例用 5 个辅助函数生成符号完整实现见 einstein.py。2. 编码 15 条线索facts 排他约束domainfacts逐条线索翻译为公式如英国人住红房子 →nat(i, british).Iff(color(i, red))完整循环见 einstein.pydomain用ExactlyOne保证每种颜色/国籍/宠物恰好出现一次见 einstein.py。problem domain.And(facts) # 问题 排他约束 ∧ 线索3. ⚠️ 示例故意埋了一个 Bug挪威人住在蓝色房子旁边这条线索作者写成了双向等价见 einstein.py代码里还贴心地留了# Careful with this one!注释# BugIff双向等价比线索更强它反向要求挪威人旁边必须有一栋蓝房子 nat(i, norwegian).Iff(color(i-1, blue) | color(i1, blue))此时求解器返回NoneUNSAT——线索本身无矛盾问题出在编码。这正是 UNSAT Core 登场的时机。四、UNSAT Core 调试三步法官方示例的调试逻辑在 einstein.py核心代码如下model get_model(problem) if model is None: # 第 1 步隔离验证——线索、排他约束各自单独可满足吗 assert is_sat(facts) assert is_sat(domain) # 各自 SAT、合取 UNSAT ⇒ 矛盾在两部分交互中 # 第 2 步把嵌套的 And 公式拉平为独立子句列表 from pysmt.rewritings import conjunctive_partition conj conjunctive_partition(problem) ucore get_unsat_core(conj) # 第 3 步打印肇事子句像读错误堆栈一样定位 print(UNSAT-Core size %d % len(ucore)) for f in ucore: print(f.serialize())conjunctive_partition定义于 rewritings.py把And(And(a, b), c)这种嵌套结构展平成子句流保证 UNSAT Core 能逐条指认来源。如何读输出三步推理官方注释einstein.py给了绝佳的推理示范找单元子句核心里唯一的原子事实是0_nat_norwegian挪威人在 0 号房——这条没问题找传播链核心含(1_color_blue ↔ 0_nat_norwegian)由 Bug 处的Iff产生——它强制1 号房必须是蓝色找冲突点另一条线索(3_color_blue | 1_color_blue) ↔ 2_nat_norwegian要求 2 号房住挪威人而ExactlyOne禁止挪威人同时住 0 号和 2 号 → 矛盾坐实修复把 Iff 改成 Implies# 修复后单向蕴含只表达挪威人旁边有蓝房子不再反向约束 nat(i, norwegian).Implies(color(i-1, blue) | color(i1, blue))重新运行模型输出经典答案——德国人养鱼房子颜色国籍宠物饮品香烟0黄挪威猫水Blends1蓝丹麦马茶Pall Mall2红英国鸟牛奶Blumemasters3绿德国 鱼咖啡Prince4白瑞典狗啤酒Dunhill五、UNSAT Core API 进阶命名模式需要逐条点名某条约束是否入核时用named模式测试用例见 test_unsat_cores.pyfrom pysmt.shortcuts import Symbol, Not, BOOL, UnsatCoreSolver x Symbol(x, BOOL) with UnsatCoreSolver(logicQF_BOOL, unsat_cores_modenamed) as solver: solver.add_assertion(x, nameda1) solver.add_assertion(Not(x), nameda2) solver.solve() print(solver.get_named_unsat_core()) # {a1: x, a2: !x}UnsatCoreSolver基类与校验逻辑见 solver.pyZ3、MathSAT 等具体实现分别在 z3.py 与 msat.py。六、UNSAT Core 踩坑清单现象原因与解法SolverNotConfiguredForUnsatCoresError用的是普通Solver或unsat_cores_mode未设置改用UnsatCoreSolver(unsat_cores_mode...)SolverStatusError最近一次solve()结果不是 UNSAT或solve之后又追加了断言状态已失效见 solver.py核心大小/内容每次不同正常UNSAT Core 不唯一结果依赖求解器实现只作为调试起点pysmt-install --check看不到求解器UNSAT Core 依赖具体求解器支持先装好 Z3 或 MathSAT七、总结与延伸阅读回顾 UNSAT Core 调试三板斧隔离is_sat验证子公式各自可满足锁定交互矛盾拉平conjunctive_partition把大公式拆成可指认的子句列表读核找单元子句 → 追传播链 → 对照ExactlyOne定位冲突最后把过强的Iff收敛为Implies。完整可运行示例含 Bug 与修复注释examples/einstein.py更多谜题可从 examples/README.rst 中的入门清单sudoku、puzzle、allsmt继续练手。【免费下载链接】pysmtpySMT: A library for SMT formulae manipulation and solving项目地址: https://gitcode.com/gh_mirrors/py/pysmt创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表