证明可能性
摘要
在形式化方法中解释可能性属性的概念,补充安全性和活性,并讨论它们在规范制定和模型检验中的使用。
<p>作为一名形式化方法顾问,我需要用数学方式表达系统的性质。我通常使用两个"时序算子"来完成这项工作:</p>
<ul>
<li>A(x) 表示 <code>x</code> 总是成立。例如,数据库表<em>总是</em>满足所有记录级约束,状态机<em>总是</em>在状态间进行有效转换。如果 <code>x</code> 是关于单个状态的陈述(如数据库示例,而非状态机示例),我们进一步称其为<strong>不变量</strong>。</li>
<li>E(x) 表示 <code>x</code> "最终"成立,通常指"在未来的某个时刻必然成立"。数据库事务<em>最终</em>要么完成要么回滚,状态机<em>最终</em>到达"完成"状态,等等。</li>
</ul>
<p>这些算子来自线性时序逻辑,该逻辑是表述系统性质的主流符号体系。<sup id="fnref:modal"><a class="footnote-ref" href="#fn:modal">1</a></sup> 我们喜欢这些算子,因为它们简洁地涵盖了<a href="https://www.hillelwayne.com/post/safety-and-liveness/" target="_blank">安全性和活性性质</a>,并且<a href="https://buttondown.com/hillelwayne/archive/formalizing-stability-and-resilience-properties/" target="_blank">我们可以组合使用它们</a>。<code>A(E(x))</code> 表示 <code>x</code> 无限次成立,而 <code>A(x => E(y))</code> 表示 <code>x</code> 成立能保证 <code>y</code> 在将来成立。</p>
<p>还有第三类性质,我称之为<em>可能性</em>性质:<code>P(x)</code> 表示"<code>x</code> 在这个模型中可能发生吗?" 一个表格可能拥有超过十条记录吗?一个状态机能否从"完成"状态转换到"重试"状态,即使它<em>实际上</em>并不这么转换?重要的是,<code>P(x)</code> 并不需要<em>立即</em>可能,只需在未来某个时刻可能。即使你每次只赌一美元,老虎机也有可能让你输掉一百美元。如果 <code>x</code> 是关于单个状态的陈述,我们可以进一步称其为<a href="https://en.wikipedia.org/wiki/Reachability" target="_blank"><em>可达性</em>性质</a>。为行文流畅,我将二者混用。</p>
<p><code>A(P(x))</code> 表示 <code>x</code> <em>总是</em>可能的。无论我们在系统中做了什么,我们都能让 <code>x</code> 再次发生。仅靠 <code>A</code> 和 <code>E</code> 无法做到这一点。其他有意义的组合包括:</p>
<ul>
<li><code>P(A(x))</code>:存在一个可达状态,从该状态起 <code>x</code> 总是成立。</li>
<li><code>A(x => P(y))</code>:从任何 <code>x</code> 成立的状态出发,<code>y</code> 都是可能的。</li>
<li><code>E(x && P(y))</code>:总是存在一个未来状态,其中 <code>x</code> 成立且 <code>y</code> 可达。</li>
<li><code>A(P(x) => E(x))</code>:如果 <code>x</code> 是可能的,那么它最终会发生。</li>
<li><code>E(P(x))</code> 和 <code>P(E(x))</code> 等同于 <code>P(x)</code>。</li>
</ul>
<p>有关 <code>E</code> 和 <code>P</code> 的深入讨论,请参阅论文 <a href="https://dl.acm.org/doi/epdf/10.1145/567446.567463" target="_blank">"Sometime" is sometimes "not never"</a>。</p>
<h3>使用场景</h3>
<p>可能性性质表示"好事<em>可以</em>发生",通常(在规约中)不如"坏事<em>不能</em>发生"(安全性)和"好事<em>会</em>发生"(活性)那么有用。但它仍然是一项重要性质!我最喜欢的例子:</p>
<p><img alt="一个因为系统偏好设置中断关机而无法关闭电脑的人" class="newsletter-image" src="https://www.hillelwayne.com/post/safety-and-liveness/img/tweet2.png" /></p>
<p>我发现这个概念的主要用途是作为合理性检查,确认我们正确编写了规约。例如,考虑性质"处于'重试'状态的工人最终会离开该状态":</p>
<div class="codehilite"><pre><span></span><code>A(state == 'Retry' => E(state != 'Retry'))
</code></pre></div>
<p>模型检查器检查该性质并确认它在规约中成立。太棒了!我们的系统是正确的!……除非系统永远无法<em>到达</em>"重试"状态,此时该表达式是平凡成立的。我需要验证'Retry'是可达的,例如 <code>P(state == 'Retry')</code>。注意,我不能用 <code>E</code> 来做这件事,因为我并不想说"工人必须至少重试一次"。</p>
<h3>但工具并不支持</h3>
<p>我说"我发现<em>这个概念</em>的用途",是因为我主要使用的形式化方法工具(Alloy 和 TLA+)本身并不支持 <code>P</code>。<sup id="fnref:tla"><a class="footnote-ref" href="#fn:tla">2</a></sup> 除了 <code>P</code> 不如 <code>A</code> 和 <code>E</code> 有用之外,简单的可达性性质可以通过 <code>A(x)</code> <a href="https://www.hillelwayne.com/post/software-mimicry/" target="_blank">模拟</a>。当 <code>A(!x)</code> <em>失败</em>时,<code>P(x)</code> 就<em>通过</em>了,这意味着我可以通过测试 <code>A(!(state == 'Retry'))</code> 是否找到反例来验证 <code>P(state == 'Retry')</code>。但我们不能用这种方式模拟组合算子如 <code>A(P(x))</code>,不过这类算子远不如状态可达性常见。</p>
<p>(此外,精化不保持可能性性质,但这完全是另一码事。)</p>
<p>有一点让我略感头疼:我们无法模拟"从每个起始状态出发 <code>P(x)</code> 成立"。"<code>A(!x)</code>" 只要存在一条从某个起始状态出发的路径能到达 <code>x</code> 就会失败,但其他起始状态可能无法使 <code>x</code> 成为可能。</p>
<p>我怀疑这里还存在一个先有鸡还是先有蛋的问题。由于我的工具无法验证可能性性质,我并不习惯在系统中注意到它们。我很想知道是否有人处理过代码库中可能性性质很重要的情况,特别是像 <code>A(x => P(y))</code> 这样复杂的组合。</p>
<div class="footnote">
<hr />
<ol>
<li id="fn:modal">
<p>文献中通常用 <code>[]x</code> 或 <code>Gx</code>("globally x")代替 <code>A(x)</code>,用 <code><>x</code> 或 <code>Fx</code>("finally x")代替 <code>E(x)</code>。此处使用 A 和 E 并非教学目的。 <a class="footnote-backref" href="#fnref:modal" title="回到脚注 1">↩</a></p>
</li>
<li id="fn:tla">
<p>关于在 TLA+ 中添加该算子的讨论,参见<a href="https://github.com/tlaplus/tlaplus/issues/860" target="_blank">此处</a>。 <a class="footnote-backref" href="#fnref:tla" title="回到脚注 2">↩</a></p>
</li>
</ol>
</div>
查看缓存全文
缓存时间: 2026/05/16 03:40
# 证明什么是可能的
来源:https://buttondown.com/hillelwayne/archive/proving-whats-possible
作为一名形式化方法顾问,我需要用数学方式表达系统的性质。通常我会使用两个"时序算子":
- A(x) 表示 `x` 始终为真。例如,数据库表*始终*满足所有记录级约束,状态机*始终*在状态之间进行有效转换。如果 `x` 是关于单个状态的陈述(如数据库示例,而非状态机示例),我们进一步称其为**不变量**。
- E(x) 表示 `x` "最终"为真,通常指"在未来某个时刻保证为真"。数据库事务*最终*完成或回滚,状态机*最终*到达"完成"状态,依此类推。
这两个算子来自线性时序逻辑,这是表达系统性质的主流符号¹。我们喜欢这些算子,因为它们优雅地覆盖了安全性和活性性质²,并且我们可以组合使用它们³。`A(E(x))` 表示 `x` 无限次为真,而 `A(x => E(y))` 表示 `x` 为真能保证未来的 `y` 为真。
还有第三类性质,我将称之为**可能性**性质:`P(x)` 表示"在这个模型中 `x` 能否发生"?一个表是否可能拥有超过十条记录?状态机是否可能从"完成"转换到"重试"——即便它*实际上*并未发生?重要的是,`P(x)` 不需要*立即*可能,只需在未来某个时刻可能即可。在老虎机上赌 100 美元是有可能输掉的,哪怕你每次只赌 1 美元。如果 `x` 是关于单个状态的陈述,我们可以进一步称之为**可达性**性质⁴。为了行文流畅,我会交替使用这两个术语。
`A(P(x))` 表示 `x` 是*始终*可能的。无论我们在系统中做了什么,我们都能够再次让 `x` 发生。仅用 `A` 和 `E` 是无法表达这个含义的。其他有意义的组合包括:
- `P(A(x))`:存在一个可达状态,从该状态起 `x` 始终为真。
- `A(x => P(y))`:从任何 `x` 为真的状态出发,`y` 都是可能的。
- `E(x && P(y))`:未来总有一个状态,其中 `x` 为真且 `y` 可达。
- `A(P(x) => E(x))`:如果 `x` 曾经可能,那么它最终会发生。
- `E(P(x))` 和 `P(E(x))` 等同于 `P(x)`。
关于 `E` 和 `P` 更深入的讨论,请参见论文《"有时"并非"从不"》⁵。
### 使用场景
可能性性质意味着"好事*可能*发生",这在规范中通常不如"坏事*不可能*发生"(安全性)或"好事*将*发生"(活性)那么有用。但它仍然是一个重要性质!我最喜欢的例子:
有个人因为系统偏好设置打断关机而无法关闭电脑。
我发现这个想法的一大用途是作为检查规范编写是否正确的"合理性检验"。假设我采用这样一个性质:"处于'重试'状态的 Worker 最终会离开该状态":
```
A(state == 'Retry' => E(state != 'Retry'))
```
模型检查器验证这个性质,并确认它对规范成立。太好了!我们的系统是正确的!……除非系统永远*无法*到达"重试"状态,在这种情况下该表达式是平凡为真的。我需要验证 'Retry' 是否可达,例如 `P(state == 'Retry')`。注意我不能用 `E` 来做这件事,因为我不想说"Worker 必须至少重试一次"。
### 然而并不受支持
我说"我发现*这个想法*的用途",是因为我主要使用的形式化工具(Alloy 和 TLA+)本身并不支持 `P`⁶。除了 `P` 不如 `A` 和 `E` 有用之外,简单的可达性质可以用 `A(x)` 来模仿。`P(x)` 在 `A(¬x)` *失败*时*通过*,这意味着我可以通过测试 `A(¬(state == 'Retry'))` 是否找到反例来验证 `P(state == 'Retry')`。但我们*不能*用这种方式模仿像 `A(P(x))` 这样的组合算子,不过这类算子远不如状态可达性常见。
(此外,精化不会保持可能性性质,但这完全是另一回事了。)
有点让我感到棘手的是,我们无法模仿"从每一个起始状态出发 `P(x)` 都成立"。"A(¬x)" 失败意味着至少存在一条从某个起始状态出发通向 `x` 的路径,但其他起始状态可能无法使 `x` 成为可能。
我怀疑这里还存在一个鸡生蛋蛋生鸡的问题。由于我的工具无法验证可能性性质,我反而不习惯在系统中注意它们。如果有人在使用代码库时认为可能性性质很重要,尤其是像 `A(x => P(y))` 这样复杂的情况,我很乐意听听。
相似文章
智能体技能形式化验证方法:迈向可机械检查的能力包含性证明的三层架构
本文提出了三种可组合的方法——抽象解释、精化类型和SMT有界模型检验——用于机械验证由大语言模型驱动的智能体技能行为是否包含在其声明的能力范围内,从而弥合了配套论文中提出的形式化验证级别之间的差距。
OProver:一个统一的代理式形式定理证明框架
OProver是一个统一的框架,用于Lean 4中的代理式形式定理证明,通过使用经过验证的证明和编译器反馈进行训练,迭代地改进证明生成,在多个基准测试中取得了最先进的结果。
假设弱化性质
本文探讨了为什么在规范或测试中添加假设会从逻辑上弱化所得性质,使用了逻辑蕴含以及来自形式化方法和 Rust 的示例。此外,还讨论了尽管存在这种弱化,仍使用假设的实际原因。
发现与证明:Lean 4中困难模式自动定理证明的开源智能体框架
本文介绍了 Discover and Prove (DAP),一个用于 Lean 4 自动定理证明的开源智能体框架,针对"困难模式"问题进行优化——即在构造形式化证明前必须独立发现答案。该工作发布了新的困难模式基准变体,达到最先进的结果,同时揭示了 LLM 答案准确率(>80%)与形式化证明器成功率(<10%)之间的巨大差距。
评估Lean 4中证明自动形式化的鲁棒性
本文评估了在全局和局部扰动下,Lean 4中证明自动形式化模型的鲁棒性,发现当前基于LLM的模型对扰动敏感,且常常无法忠实地反映局部变化。