自动推理笔记

命题逻辑(还记得我吗?)

命题公式

由若干布尔变量和运算符($\neg, \cap, \cup, \rightarrow, \leftrightarrow$)得到的公式

有一些命题,我们能否从中得到另一个命题

将问题转化成更“正式”的问题

使用自动化程序解决“正式”的问题

 

通过形式化,可以将每个概念转化成符号

 

方法1:真值表证明该式为重言式,如果能证明是重言式,那么就能证明

但是,这个方法是O(2^n)的,虽然在这里还可以,但是如果n再大就不好用了

方法2:可满足式

一个式子是可满足的,如果其值可以为真

反之,如果其值恒为假,那么则不可满足

 如果一个式子是重言式,那么这个式子的否命题就是不可满足式

判定一个式子是否是可满足式的任务被称为SAT(short for satisfiability)

许多问题可以变成SAT,比如想要检测一个微处理器是否运作正常,可以用另一个微处理器与其比较,如果非(micro1<->micro2)不可满足

不过,所有已知的方法最差的情况下都是指数级的

鸽巢公式:一个不可满足公式,但想要证明公式本身不可满足比较困难

有n*(n+1)个布尔变量

设$C_n=\bigwedge_{j=1}^{n+1}(\bigvee_{i=1}^{n}P_{ij})$

$R_n=\bigwedge_{i=1...n, 1\leq j <k \leq n+1}(\neg P_{ij} \cup \neg P_{ik})$

$PF_n=C_n \cap R_n$

 

Same for disjunction

可以把上面的式子换成矩阵

 

于是,$C_n$的意思就是,对于第j列,至少有一项为真,而j从1至n,所以就是每一列都要有至少一项为真

所以至少有n+1项为真

而对于$R_n$的意思,对于第i行而言,每两个变量里至多有一个为真,所以n行最多有n个变量为真

而$PF_n$要求$C_n$和$R_n$都为真,这是不可能的。

鸽巢原理:如果有n+1个鸽子进了n个巢穴,至少有一个巢穴进了两个鸽子

注意到,所有的约束都是必须的,如果有一个约束被移除,那么式子就变成可满足式了

这里我们也看到了$C_n$是至少一个而$R_n$是至多一个,我们之后会经常用到。

使用计算机证明鸽巢式是很困难的

接下来我们会见识一种无关可满足性,常用自动推理解决的问题

模型验证:

 

如果二者为真,那么给二者赋假

问题:能否从全真到全假?

同样,类似的问题可能有很多状态,如果想要一个一个搜索过去会很困难

模型检测:

有有限个状态,有起始状态、转移方式,一些需要到达的状态,问是否能到达

程序检验:可能需要同时考虑多种状态转移,非确定性的状态

我们称初始状态的描述以及状态转移的描述为模型,因此检测一个模型是否具有某些性质就叫模型检验

将模型的描述形式化为符号,之后作检验的方法称为符号模型检验

自然,我们检验可以用SAT,但我们需要考虑更好的模型,这被称作重要的(Crucial)模型

SAT的解法:Z3、Yices

 一种基本的符号模型检测方法:Binary Decision Diagrams(二进制决策图),NuSMV。

并非所有命题都可以转化成命题逻辑,首先是SMT:Satisfiablity Modulo Theories,可满足性模型理论,这里主要研究线性不等式的理论,即变量不只是布尔,而是整数和实数

注意,SMT依然适用SAT的一些术语,比如说给整数赋值如果能满足,那么依然称为SAT

SMT-LIB语法

 

 

注意是语法使用的是前缀表达式,2*a=(* 2 a)

 

 

SMT可以转化成SAT解决

当然,还有比命题逻辑更难的逻辑,谓词逻辑

 不再限制域在数值上,而是在集合上

$\forall$ and $\exists$

等式逻辑

 基本上就是数理逻辑的完整部分了

模型逻辑/时序逻辑(temporal logic)

Computation Tree Logic,计算树逻辑

首先是命题逻辑

时间复杂性和空间复杂性

magnitude,规模

$f(n)=O(g(n))$

注意$\Omega$是指式子大于等于

SAT无线性时间

决策问题

决策问题是指你问一种事,回答你是或者否

P是指所有有多项式算法的决策问题,即在多项式时间内可以回答你问的事

引入证据(certificate,验证方法,中文常常称作解而且不这么说)一词

NP问题是指:

如果一个决策问题的正确结果是否,那么其一定没有可供验证的解

如果正确结果是真,那么我们可以在多项式时间内验证某个解是否符合要求

以SAT为例子,如果决策问题结果本身就绝对不可满足,那么自然不可能找到一个解,而如果可满足,那么才能多项式时间内验证

对于大部分问题这挺显然的,不过对于素数判定似乎有点问题?

非确定性线性时间

很显然,P问题全都是NP问题

但是我们无法确定NP问题是否都是P问题

NP完全问题:如果有一个NP问题A,而且假设A也是P的,那么可以得到P=NP,那么称A为NP完全。或者也就是说所有NP问题可以归约到这个问题上

(注意,规约并不是双向的,考虑解二元一次方程和解一元一次方程,二元一次方程归约到一元一次方程是可能的,但反过来是不可能的)

SAT是NP完全的,因此许多其他问题也是NP完全的。此外,SAT可以规约到其他一切问题上,但反之则不可

由于NP完全问题存在,所以不太可能有P的

SAT是地基

一些典型NPC问题:走过所有点,是否有路径≤n?

这一问题与TSP密切相关(自我练习TSP<->SAT)

给一个无向图,是否存在一个哈密尔顿回路,经过每个点恰好一次?

给一些东西,能否将其分成两组,两组重量相同?

命题逻辑中的算数:二进制算术=命题逻辑算数

由于算数可以转化成二进制算术,而二进制算术可以使用SAT来考虑性质,所以我们可以将算术问题转化成SAT

 

乘法也可以做,不过考虑到乘法其实是加法的重复,所以可以重用加法,我们后面会进一步了解这方面的内容

八皇后问题

与常规不同,我们只考虑问题怎么用SAT/SMT表示,让求解器自己来解答

$p_{ij}$表示这个位置是否有皇后

自然语言好说,怎么用SMTLIB表达?

对于某一行而言,应该至少有一个为true,而且至多有一个为true

至少:$p_{i1} \cup p_{i2} ... \cup p_{in}$

至多(对每一对而言,至多有一个为真):$\bigwedge_{i=1...n, 1\leq j <k \leq n+1}(\neg p_{ij} \cup \neg p_{ik})$

此外,还要求每一行都如此,所以:

$\bigwedge_{j=1}^{n}(\bigvee_{i=1}^{n}p_{ij})$

$\bigwedge_{i=1}^{n}\bigwedge_{1\leq j <k \leq n+1}(\neg p_{ij} \cup \neg p_{ik})$

对于列也同样:ij交换,其他的一样

对角线:每个对角线至多有一个皇后,对角线上元素的下标的和/差相同

如果不加限制,有若干个值会被多次枚举,故专门限制:

$\bigwedge_{0<i<i' \leq n}(\bigwedge_{j, j': i+j=i'+j' \cup i-j=i'-j'}\neg p_{i,j} \cup \neg p_{i'j'})$

八皇后结果之一:

(
(define-fun p ((x!0 Int) (x!1 Int)) Bool
(ite (and (= x!0 1) (= x!1 4)) true
(ite (and (= x!0 2) (= x!1 2)) true
(ite (and (= x!0 3) (= x!1 7)) true
(ite (and (= x!0 4) (= x!1 3)) true
(ite (and (= x!0 5) (= x!1 6)) true
(ite (and (= x!0 6) (= x!1 8)) true
(ite (and (= x!0 7) (= x!1 5)) true
(ite (and (= x!0 8) (= x!1 1)) true
false)))))))))
)

这里是这样的意思:p是一个function,返回bool,当x0是1且x1是4时,给出true,否则(第一行的else),如果(第二行的if)x0时2且x2是2,给出true……直到最后如果全不满足,p的值赋为false

利用纯SAT解决算术问题

加法、减法

能用SMT为啥用SAT?

1. 有趣

2. 有关乘法的方法与限界模型检测有关

3. 硬件检测中SAT优于SMT

由于SAT里只有布尔变量,所以我们应该用二进制表示一个十进制数

使用命题公式表示二进制加法:

注意这里$a_n$是指最后一位

 后半部分当且仅当后三个都是1或者有一个是1时才会是1

 如果abc中至少有两个是一,那么ci-1会发生进位

此外,为了让进位能够落下去,我们还需要限定$\neg c_n$和$\neg c_0$。如果发生第一位的进位,我们可以考虑适当增加n的大小

上述所有限制的交即为计算式(注意不是整个公式),再交上A和B的位数即为最终结果

此外,我们可以赋d和a(而非a和b)的值,使得整个式子变成算减法

乘法

与加法一样,我们也要构筑三个变量abd,并探讨其关系,并构筑一个计算式

需要更多额外变量,不只是需要进位

二进制乘法只有1和0,其他的和乘法其实一样

 

虽然用传统的加法也能做,但在这里我们使用快速快速加以避免复杂的进位问题

从左到右处理b

 

 可以用循环不变式证明

可以将数字拆成二进制的数字,之后转化为变量

比如A=7=111(2),$\vec A=(1, 1, 1)$

限界模型检测:对于某一个可变布尔变量x,引入一个数组$x_i$表示第i步后该变量的值

在本算法中,对r和s进行如此操作,r在上文已经写过,s是每一步r的两倍

对于$\vec y=2 \vec x$,我们当然可以说是$\vec y=\vec x + \vec x$,然而更简单的方法还是,每次加操作等于全向左移一位

 注意这里x原本的位数是n-1,这句话表示,如果这个式子满足,那么有y=2x

引入$\vec r_{i}, \vec s_{ij}$表示r和s在i步后的值(允许i=0,此时为0)

在第n步后的值就是$r_n$

 

$mul(\vec a, \vec b, \vec r_{n})=\bigwedge_{j=1}^{n} \neg r_{0j} \cap \bigwedge_{i=0}^{n-1}(dup(\vec r_{i}, \vec s_{i}) \cap (b_{i+1} \rightarrow add(\vec a, \vec s_i, \vec r_{i+1})) \cap (\neg b_{i+1} \rightarrow \bigwedge_{j=1}^{n} (r_{i+1, j} \leftrightarrow s_{i, j})))$

如此,可以用SAT做各种数的算数

比如分解一个数t,就是找两个数乘积为t。

 

 

 如果r是素数,那么fac(r)是不可满足式

找出可满足式的解往往比找不可满足式在实践中来的要快

此外,还可以用SAT解决一些验证程序正确性的问题,不过与SAT验证算术问题一样,需要先将所有变量用布尔进制表示。注意使用SAT时需要标明最大位数。

SMT的一些应用

矩形覆盖

若干小矩形,问是否可以放入一个矩形里(下文中的矩形是“立起来”考虑的,所以是宽和高,当然也可以说是长和宽)

给小矩形编号,$w_i$表示第i个矩形的宽,$h_i$表示第i个矩形的高,以及矩形左下角点$(x_i, y_i)$的坐标

 

一个4*6的矩形

首先要定义大矩形的位置,不妨设大矩形左下角的点为$(0, 0)$,宽为W,高为H

那么每一个矩形都应该在大矩形里,所以$x_i>0$且$x_i+w_i \leq W$,$y_i>0$且$y_i+h_i \leq H$

矩形ij重叠的条件是,不保证$x_i, y_i, x_j, y_j$的具体情况,$x_i+w_i > x_j$(第一个矩形的右侧边在第二个矩形的左侧边的右侧)且$x_i<x_j+w_j$第一个矩形的左侧边在第二个矩形右侧边的左侧);且$y_i+h_i>y_j$(第一个矩形上侧边在第二个矩形下侧边的上侧)且$y_i<y_j+h_j$(第一个矩形的下侧边在第二个矩形上侧边的下侧)

这四个条件同时满足即为两个矩形相交,不希望有相交,故取$\neg$。

此算法除了可以在有解时取得解,还可以在无解时输出无解(这点许多其他算法很难做到,比如遗传算法)

如果可满足,会输出$x_i, y_i, w_i, h_i$。

这类问题永远用脚本生成,不要自己写全部公式

对于一行里只能出现一个的情况,我们依然说两两不同,此外SMT里还有一个特殊的语法,叫(distinct ...)

构造数独的方法:在一个老数独上,删除一定元素,直到能找到一个与老数独本身的解不同的解。

做题时如果不确定不妨引入新变量

 

 同样,不过这里是一个人不能在一个时间内做两个工作的约束,注意这里不应该使用时间本身作变量

如何表示“最小化”某个东西?

引入新变量(这里是T),使得所有值小于新变量

但是这样无法判定T的值,方法1:二分T的值。方法2:SMT-LIB自带(minimize)字段。

限界模型检测:利用SAT和SMT验证程序(可以是广义上的程序)的正确性

弹球游戏:以一个弹珠开始,每一步要么加一个,要么将弹珠数量加倍

能否达到正好1000个弹珠?显然可以,每次加一个总会到达1000个。

怎么走才能最快到正好1000个弹珠?

利用SMT解决:引入k为步数,引入M[i](i从0至k),M[i]表示i步后弹珠数量

M[0]=1,M[k]=1000

与之前一样,我们写明公式:$(M[i]=M[i-1]+1) \cup (M[i]=2M[i-1]), i=1... k$

整个公式就是$M[0]=1 \cap M[k]=1000 \cap \bigwedge_{i=1}^{k}((M[i]=M[i-1]+1) \cup (M[i]=2M[i-1]))$

特别的,我们调用k=1, 2, 3…… 13,可以发现Z3运行下整个式子是不可满足的。

这里的调用应该就是$\cap k=x$,k=14时有解。

再一次的,二分查找

我们引入“状态”的概念,“状态”通过若干变量描述,在这里“状态”指代弹珠数目

步骤,指状态的改变

步骤+状态=迁移系统

有一些初始状态(初始的变量值)

有一些性质需要检查(k步后刚好得到1000个弹珠)

 

可以用状态图表示

模型检查是一种自动证明或证否的方法,通过描述迁移系统路径上的某些时间逻辑来进行

典型的写法$G\phi$,其中$\phi$是一个对于任何路径都适用的公式

如果我们想要知道s是否可达,检查$G\neg\phi$,如果成立,那么$\neg\phi$全域成立

否则,s可达,我们应该能输出一条合适的路径

限界模型检查:只考虑路径长度为k的步骤,并通过SAT/SMT检查,其中引入若干变量描述每一步后值的变化,$v_i$。

同时,描述初始值的要求,$v_0$。描述最终值的限制,$v_k$。

同时对于时间序上的每一步,描述$v_i$如何通过$v_{i-1}$表示。

特别的,当值不变时,特意使值保持不变$v_i=v_{i-1}$。

一般来说,我们是处理固定数量步骤的循环的程序时用有界模型检查,不过实际上也可以对不限制数量循环的做——同时得到一些近似的结论。

在NuSMV中使用-bmc可以自动解决这一问题(值得一试

当我们需要在程序中满足一些性质时,引入霍尔三元组。霍尔三元组$\{P\}S\{Q\}$,其中P是前条件,S是程序,Q是后条件。P是要求在S运行前应满足的条件,Q是S运行后应满足的条件。如果我们想要验证程序是否具有某些性质Q,我们就要验证$P_{0} \cap S \cap \neg Q_{m}$是不可满足式(或者可以看作$\neg ((P_{0} \cap S) \rightarrow Q_{m})$是不可满足式)。

注意,这个过程中仍需要用时间序表示状态。

例子:现有程序S:for j=1 to m-1, a[j+1]=a[j],求证Q 最后a[m]=a[1]。

P:True

S:用$a_{ij}$表示j次迭代后$a[i]$的值,有 $a_{j+1, j} \leftrightarrow a_{j, j-1} \cap \bigwedge_{i=1, i≠j}^{m} a_{i, j} \leftrightarrow a_{i, j-1}$ (整个式子就是$ \bigwedge_{j=1}^{m-1} (a_{j+1, j} \leftrightarrow a_{j, j-1} \cap \bigwedge_{i=1, i≠j}^{m} a_{i, j} \leftrightarrow a_{i, j-1})$

Q:$a_{m, m-1} \leftrightarrow a_{1, m-1}$

归结

归结方法至今仍是许多求解器的基础,同时也是许多可满足式验证中的基本方法。

首先,归结并不能对任何命题公式使用。只适用于合取范式(Conjunctive normal form, CNF)。

CNF是若干子句的合取(交),其中每个是若干文字(符号或$\neg$符号)的析取。

$\bigwedge_{i} (\bigvee_{j} l_{ij})$,其中$l_{ij}$是文字。

具体例子$(p \cup q) \cap (\neg p \cup \neg q) \cap (p \cup \neg q) \cap (\neg p \cup q)$

此为不可满足式

任何式子都可以转为CNF式子,通过Tseitin算法,其中可满足性不变。

通过这一转化,归结方法也就适用于任何命题了。

如何通过CNF检查SAT?

我们认为为真的子句是公式的属性,假设整个公式可满足,为真,那么每个子句都必须为真

由于子句是析取,所以每一个子句中至少应该有一个文字为真。

根据已有的子句,我们希望推导出新的子句并得到一个矛盾,特别的,在CNF中,空子句与矛盾相同,意味着不可满足

如果可以推导出新子句,那么说明矛盾,故CNF不可满足

我们只用到归结方法:如果CNF中有$p \cup V$和$\neg p \cup W$,可以自动得到新析取式子$V \cup W$。

我们具体分析,如果整个式子是可满足式,那么如果p为真,那么W必定为真,此时$V \cup W$必定为真。如果p为假,那么V必定为真,此时$V \cup W$仍然必定为真。而对于CNF式而言,析取一个真式是不会影响原本可满足性的。

$\frac{p \cup V, \neg p \cup W}{V \cup W}$。

子句中,顺序并不重要

此外,$p \cup p = p$。

因此我们可以将子句视作文字的集合,而整个CNF就是集合的集合

例子:

 

79矛盾(也可以归结得到空子句),因此不可能

我的想法是,一个式子要么可满足,要么不可满足,如果可满足,那么可以用式子推,如果推出不可满足,说明假设错了,说明不可满足。

注意:很多式子有可能有很多得到空子句的方法,因此不只是有一个解答

形如$p \cup q$且$\neg p \cup \neg q$的式子可以归结,但没什么用,因为归结后会产生恒真式

如果一个子句仅含单个文字,我们便可以从$\neg l$中得到其他归结式子

我们可以证明该式子是可靠且完备的,可靠已在前文看到(漏一个证明 7-22:哪呢?)

归结只能回答是否可满足,不能直接回答其他问题

经典变换:如果想要证明某个式子是重言式,可以将其否定,转化为不可满足式

证明某个公式包含另一公式,比如$p \rightarrow q$,即证明$p \cap \neg q$不可满足

即,如果这个公式恒真,由于$p \rightarrow q$等价于$\neg p \cup q$,那么式子等价于证明$\neg p \cup q$恒真,即$p \cap \neg q$不可满足。

同样证明两个式子等价,等价于证明两个式子互相蕴含,等价于双蕴含式的否定是不可满足式

 

如果两个式子不相同,那么整个式子就变得可以满足了

如果式子不是CNF,转换:

 

这里实际上是等价于,我们要证明$p \rightarrow q$,根据上面的转化,就相当于证明长式子是不可满足式

可以通过不停的迭代只有单个符号的式子,不断简化式子,直到出现空式子

永远优先归结只有单个符号的clause。(单位归结)

David-Putsman方法(注意不是DPLL):

简而言之,每次选择一个变量,然后优先消除所有含有这个变量的子句对。

DPLL是一种用CNF判断式子是否可满足的算法

这个算法就是,有单位用单位,没有单位就挑一个变量出来分情况讨论分析,然后直到结束

不过注意,我们归结时同时也将单位的子句归结掉,因为单位注定了其为真

 

 

 

如果X为空,那么可满足

注意这里是并集合,加进来之后还是交

如果X中不含空子句,执行下面的

算法一定会停止,因为每次算法变量数量是严格减少的

最后,如果算法返回了一个可满足的,那么整个式子就可满足

DPLL转归约:如果已经有一个DPLL的证明过程,如何转成归约的证明过程?

对于不可满足式,这个比较简单,首先消单位元一定不改变可满足和不可满足性

而给某个文字赋值的行为也不会影响到不可满足性

需要证明$X \cup \{p\} \vdash \perp$和$X \cup \{\neg p\} \vdash \perp$,这样才能证明$X \vdash \perp$

首先,如果我们在某一步进行了归约,那么并进来的单位字符一定没有影响

 因此,假设我们不移除$\neg p$(或者说我们在这些式子最后加回$\neg p$并输出),最后要么得到$X \vdash \perp$,要么得到$X \vdash \neg p$

对于$\neg p$也是一样的

或者我们在这里再具体说一点,在并入新单位元的步骤时,我们直接认为不再引入单位元,但是后面的步骤按照引入单位元做,并在最后加上$\neg$单位元。这样做,我们要么最后得到一个空子句,说明不可满足,要么得到至少一个被引入单位元的否定(例如引入的单位元是p,最后会得到$\neg p$,而由于另一条线中一定会得到$\neg p$,故单位消元得到空子句)

由此,我们就得到了整个式子的归约式。使用单位元消掉的式子不会再被用到。

由此,我们发现每一个不可满足式可以通过DPLL转归约得到结果(必要),而同时我们又知道归约可以给不可满足式产生结果,所以可靠且完备。

这个方法也可以改到任何一个正规的证明中,然后还要归纳并再分类讨论。

CDCL:conflict driven clause learning,DPLL的一种高效实现。

 如果想要实现这个算法,显然最简单的是每次复制一个CNF

DPLL中最大的操作就是单位消元,自然我们又想到了让原本的CNF不要直接移除,而是打上标记什么的

基本思想:我们用一个数组M记录DPLL中被选择和导出的文字。

M扩展:

加p和非p时,会加入到M中

如果通过单位消元出了文字,那么要加入M

如果回溯,需要删掉一部分

如果l在M中出现过,我们说$M \vDash l$。

如果对于子句C中的每个l而言,$\neg l$都出现在M中,那么$M \vDash \neg C$

l是未被定义的,如果p和$\neg p$没有出现在M中过

如果M中的所有子句已经是单位子句,且有$C \cup l$且$M \vDash C$,那么根据定义,l可被加入M

$M \rightarrow Ml$,如果l还未在M出现过且$M \vDash \neg C$

 

对于Decide操作,有:

 

注意,此处的上标是指回溯的节点

 

如果在过程中,我们发现$Ml^dN$中包含一整个$\neg C$,那么我们便不能用$Ml^dN$对其做单位消元,所以需要回溯

如果$M \vDash \neg C$,且C在CNF中,那么$M \rightarrow fail$。

 根据该系统的几条规则,我们要么得到fail——不可满足,要么得到每个变量都已赋值的一个可满足解。

一个问题:这个算法本身没什么大问题,但是在给出的例子中,我们总是很聪明的挑了容易得到答案的路径,那么真实情况是什么呢

提升效率关键在于选好decide中的变量

扩展阅读

方法1:backjump,如果我们选择了p,并在之后选择了q,同时q分叉上出现了与p无关(比如矛盾是由$\neg q \cup t$和$\neg q \cup \neg t$,注意也包括p产生的新单位元)的矛盾,我们可以通过交换p和q的位置来少做一次回溯,因为如果q在这个子分叉中不可满足,那么意味着交换完毕后整个新的子分叉都不可行。

 

 看起来很复杂,实际上是原CNF产生的新子句上的单位消元

事实上,更重要的在于在这个过程中我们发现了一个新子句$C' \cup l'$,我们会将这个新子句也加入到CNF中。相当于我们利用之前已有的信息阻止后面做无用功。

由于新子句被加入,一些老子句可能会多余而被删除。

任意命题句转CNF

希望知道任何公式的可满足性——已知CNF可以用DPLL等方法解决,但是任何公式呢?

转换成CNF。

真值表法

真值表中所有为0的句子的文字取反后并,再将所有的子句交即可。注意这是观察得到的。实质上来说这里是取得这几个位置的反,但是由于很多时候文字里本身就是假的,所以也可以认为是所有0位置取了真(真实情况是,比如说当$\neg p, q, \neg r$时取了0,而CNF的表达是为真,则应该对这三项取反,所以就相当于是在为假的地方取原文字,为真的地方取否文字)。通常来说我们构造的CNF会每一个子句都有n个文字,但有时我们能获得少于n个文字的简单子句。

然而,当变量足够复杂时,我们会产生一个很复杂的CNF。例子:$p \leftrightarrow q \leftrightarrow q ...$这一公式当且仅当会有偶数个变量为假时才为真,但是总会产生$2^{n-1}$个CNF子句(n为变量个数),其中每个子句含全部m个文字。

可以反证法证明:假设有若干字句不含m个文字,那么任取一个子句C使其取值为0,同时假设其不含文字q,q与C取值无关,而且此时CNF取值为0,但通过调整q的取值可以改变原式子中字元取假的总个数,从而使得句子为真,所以不可能有某个子句不含全部m个文字。

Tseitin变换

线性变换,将任意公式转为CNF,保证原本的可满足性。

核心思想:将命题公式的一部分命名并视作一个变量,并让其参与运算。此外,小公式的CNF往往比较好得到。因此,完整思路是,将大公式拆开后转化为每个子部分的交,然后再对这些子部分取CNF。

对于每个子公式$\Psi$,若其只包含一个文字,则其命名$n_{\Psi} = \Psi$,否则,$n_{\Psi}$为一新文字,指代原式$\Psi$。

例:

$\Psi=\neg q, n_{\Psi}=\neg q$。此时等于没有变化。

$\Psi=p \cap q \cap \neg r, n_{\Psi}=A$。相当于我们引入了一个新变量代表$\Psi$。

Tseitin变换对公式$\Phi$得到的CNF由以下组成:

$n_{\Phi}$

$CNF(q \leftrightarrow \neg n_{\Psi})$,其中q是$\neg n_{\Psi}$的命名

$CNF(q \leftrightarrow n_{\Psi_{1}} \diamond n_{\Psi_{2}})$,其中q是$n_{\Psi_{1}} \diamond n_{\Psi_{2}}$的命名,而$\diamond$是四种逻辑运算符之一。

注意:此处的过程取的“类似”求导公式(但即便这么说还是很模糊),需要分解到最小,看例子:

$\underbrace{(\neg s \cap p)}_{B} \leftrightarrow \underbrace{(\underbrace{(q \rightarrow r)}_{D}\cup \neg p)}_{C}$

这个公式$\Phi$经过变换得到:

$n_{\Phi}\cap$

$CNF(n_{\Phi} \leftrightarrow (B \leftrightarrow C)) \cap$(此处为对$\Phi$规则3)

$CNF(B \leftrightarrow (\neg s \cap p)) \cap$(此处为对B用规则3,注意上个子句里已有B出现,但仍需对其子式进行CNF)

$CNF(C \leftrightarrow (D \cup \neg p)) \cap$(同样,对C用得到D,所以仍需再用)

$CNF(D \leftrightarrow (q \rightarrow r))$

证明Tseitin变换会保证可满足性:

假设$\Phi$是可满足,取一组可满足的变量值给每个子式,且让每个引入新文字的真值跟随式子值变化,那么有:

$n_{\Phi}$为真(因为这和原式子的值相同)

$q \leftrightarrow \neg n_{\Psi}$为真,因为q是后者的命名,如果后者取假,前者会取真,整个式子为真,反过来如果后者取真,前者会取假,整个式子仍为真。因此其CNF也一定为真。

同样,根据定义,此时$q \leftrightarrow n_{\Psi_{1}} \diamond n_{\Psi_{2}}$为真。同样,其CNF也一定为真。

因此,原式的一个成真赋值一定能让其Tseitin变换式为真。

反过来,假设$T(\Phi)$是可满足式,取一组可满足的变量值给每个文字,同时将在原式中出现的对应文字赋相同的值。

由于$T(\Phi)$会满足$CNF(q \leftrightarrow n_{\Psi_{1}} \diamond n_{\Psi_{2}})$,那么也一定满足$q \leftrightarrow n_{\Psi_{1}} \diamond n_{\Psi_{2}}$(对$\neg$的情况也同理),因此$q \leftrightarrow n_{\Psi_{1}} \diamond n_{\Psi_{2}}$会为真。根据定义,当这个条件满足时,q($n_{\Psi}$)的值是$\Psi$的值。

而$n_{\Phi}$是$\Phi$的值,所以$\Phi$此时为真。

综合以上,二者真值相同(当且仅当)。

一个方便的转换:

$CNF(p \leftrightarrow \neg q) = (p \cup q) \cap (\neg p \cup \neg q)$

$CNF(p \leftrightarrow (q \cap r)) = (p \cup \neg q \cup \neg r) \cap (\neg p \cup q) \cap (\neg p \cup r)$

$CNF(p \leftrightarrow (q \cup r)) = (\neg p \cup q \cup r) \cap (p \cup \neg q) \cap (p \cup \neg r)$

$CNF(p \leftrightarrow (q \leftrightarrow r)) = (p \cup q \cup r) \cap (p \cup \neg q \cup \neg r) \cap (\neg p \cup q \cup \neg r) \cap (\neg p \cup \neg q \cup r)$

此外,Tseitin变换不存在指数爆炸问题,实际上甚至和原公式同阶。

不只是CNF,甚至是3CNF。

单纯形法

处理线性不等式。

SMT中常用单纯形法解决问题,单纯形法不仅用来解决线性不等式,一般更用来解决线性规划。

在线性规划中,我们不仅关心是否有解,还关心最优解如何。

在实数$x_1, x_2, ... x_n \geq 0$的问题中,我们想要对目标线性函数$v+c_1x_1+c_2x_2+...+c_nx_n$找到一个最大值,使其满足k个限制$a_{i1}x_1+a_{i2}x_{2}+... \leq b_i$(其中$c_i, b_i, a_{ij}$为给定实数,且有$b_i \geq 0$。

在这类优化问题中,如果保证$b_i \geq 0$,我们至少有一个全0的解(基础解)。

变体转化

一些将变体转化的方法:

不等式约束出现≥:×-1

不等式约束出现=:≥+≤

最小化而非最大化目标函数:目标函数×-1

某个变量的取值范围为负无穷到正无穷:引入两个新变量$x_1, x_2$,让两个新变量表示原变量x且两个新变量满足≥0。

如果没有基础解:后文会介绍。

松弛形式

由于处理等式比处理不等式方便,将原本的约束$a_{i1}x_1+a_{i2}x_2... \leq b_i$转化为$y_i=b_i-(a_{i1}x_1+a_{i2}x_2...)$,同时引入一个新的不等式$y_i \geq 0$。

这种有处理后等式的线性不等式称为松弛形式。

在松弛形式中,若$y_i=b_i$,即$x_{j}=0$,称此为基础解。

此时,所有$y_i$为基变量,所有$x_j$为非基变量。

单纯形算法包含重复进行转轴(pivot)操作,即将基变量和非基变量的性质对换,对换后,目标函数的值会增长,等到目标函数的值不再增长,我们就找到了最优解。

给出一个例子,看看单纯形到底在干什么:

最大化$z=3+x_1+x_3$

满足:

$-x_1+x_2-x_3 \leq 2$

$x_1+x_3 \leq 3$

$2x_1-x_2 \leq 4$

转化为松弛形式,有:

$y_1=2+x_1-x_2+x_3$

$y_2=3-x_1-x_3$

$y_3=4-2x_1+x_2$

$y_1, y_2, y_3 \geq 0$

基本解为:$x_1=0, x_2=0, x_3=0, y_1=2, y_2=3, y_3=4$

选择原式中增加幅度最小的变量,在这里$x_1$和$x_3$增加幅度一样,故选择$x_1$。对于$x_1$,由于我们要保证$y_i \geq 0$,因此三个式子对$x_1$的取值限制分别为无限制、$x_1 \leq 3$、$x_1 \leq 2$,故使x_1取值2,并与$y_3$式子互换。

具体变项方法:先将第三式原式转换:

$x_1=2-\frac{1}{2}y_3+\frac{1}{2}x_2$

接下来将新得到的式子代入三个原本的式子和目标函数中,有:

$z=5-\frac{1}{2}y_3+\frac{1}{2}x_2+x_3$

$y_1=4-\frac{1}{2}y_3-\frac{1}{2}x_2+x_3$

$y_2=1-\frac{1}{2}x_2-x_3+\frac{1}{2}y_3$

$x_1=2-\frac{1}{2}y_3+\frac{1}{2}x_2$

那么我们得到一个新的关于$y_1, y_2, x_1$的新松弛式。由于z的最小值发生了变化,我们认为我们取得了一定进展。

接下来,我们选择z中增幅最小的$x_2$做转轴。三个式子对其取值约束分别为$x_2 \leq 8$、$x_2 \leq 2$、无限制,故选择$y_2$和$x_2$式子互换,将第二个式子转换:

$x_2=2-2y_2+y_3-2x_3$

代入,有:

$z=6-y_2$

$x_2=2-2y_2+y_3-2x_3$

$y_1=3+y_2-y_3+2x_3$

$x_1=3-y_2-x_3$

现在,z的值不可能比6更大,因为$y_2$最小取值为0。此时基础解正好得到z=6(其中$x_3, y_2, y_3=0, x_2=2, y_1=3, x_1=3, z=6$。

更一般的,对于求解式子$z=v+\sum_{j=1}^{n}c_jx_j$最大值,满足不等式组$y_i=b_i+\sum_{j=1}^{n}a_{ij}x_{j}$($b_i \geq 0)$的问题,只要z中还存在正项$c_j$,我们就对正项的非基础变量找到限制其最严重的式子,并做转轴操作,同时维持松弛形式,直到不存在正项为止。

一些注意事项:

可能不存在一个有效解,即解的范围为无穷

每次转轴操作时间复杂度是线性的,即n个式子全部遍历一遍,$O(kn)$(k为不等式个数)。即便有1000个式子,也只是多项式的算法。

在最坏情况下(很少发生),整个算法是指数级的,然而在实践中一般认为单纯形是多项式的。

此外还有椭球法等方法,最坏也是多项式的,但是因为常数等问题,实践中反而不如单纯形。

使用单纯形验证可行性问题

在SMT中,我们常常会遇到只探讨是否可行且没有基础解(例如$b_i \leq 0)$的问题。

对一组不等式$a_{i1}x_1+... \leq b_i$(其中并非所有$b_i \geq 0$)。

方法:

第一部分:引入新变量z≥0,并在每个不等式后-z。

引入新变量后,由于我们可以将z值取得很大,所以该新式子总有解。

此时,我们有定理:

原式有解,当且仅当引入新变量的式子中-z最大值为0。

从右到左:右式恒有解,当-z=0时,右式=左式,故左式也有解

从左到右:原式有解,而z取值后右侧恒有解,由于左侧式子-z只会减小,而z≥0,所以-z最大为0,即此时与左式相同。

也就是说,我们最大化-z,只要其值为0,那么原式就有解。

由于我们还是没有初始解,引入第二个技巧:我们先将z和b值最负的y转轴。可以证明转轴后所有新$b_i \geq 0$,那么就可以和以往一样了(证明也很简单,因为这里y_i=$b_i - ... +z$,置换后有$z = y_i ... -b_i$,这时其他式子$-b_i$(最小的)就总得到正值,此时就有基础解了)。

例子:找到x,y满足下列不等式的可行值

$-x-3y \leq -12$

$x+y \leq 10$

$-x+y \leq -7$

由于出现了-12和-7,没有基础解,引入-z:

$-x-3y-z \leq -12$

$x+y-z \leq 10$

$-x+y-z \leq -7$

将上述三式转为松弛形式:

$y_1=-12+x+3y+z$

$y_2=10-x-y+z$

$y_3=-7+x-y+z$

第一轮转轴与第一组转轴,因为b最小。

得到$-z=-12+x+3y-y_1$

且有:

$z=12-x-3y+y_1$

$y_2=22-2x-4y+y_1$

$y_3=5-4y+y_1$

现在有基础解,且z中有多个正值c,所以可以做转轴。

此时三个式子对x的约束分别是12、11和无,所以$y_2$和x做转轴,有:

$x=11-\frac{1}{2}y_2-2y+\frac{1}{2}y_1$

$-z=-1-\frac{1}{2}y_2+y-\frac{1}{2}y_1$

$z=1+\frac{1}{2}y_2-y+\frac{1}{2}y_1$

$y_3=5-4y+y_1$

最后对y和z做转轴,有:

$-z=-z$

$x=9-\frac{3}{2}y_2-\frac{1}{2}y_1+2z$

$y=1+\frac{1}{2}+\frac{1}{2}y_1-z$

$y_3=1-y_1-2y_2+4z$

由此得到基础解为x=9,y=1,$y_3=1$,原式有解。

完整的线性优化问题:给一组不等式和一线性函数,判断是否有解,如果有解,求函数最大值。

先用上文的是否存在解的方法做,做完后以此为基础解算线性函数。

在SMT中使用单纯形

CDCL在CNF上进行,CNF是变量析取式的合取,SAT上的原子为变量,而SMT里原子为线性不等式。

CDCL的各种方法也均使用原子变换后的式子。

在CDCL中,我们常常会有$M \vDash \neg C$,即C中任意元素都与M矛盾,在SAT里,这里的矛盾是同时有真假两种变量。而SMT里,检查矛盾等价于检查是否几个线性不等式可以一同满足。

注意现在的单元子句是一个不等式,而非一个变量。

还可以混用SMT和SAT,还可以用Tseitin转换转成CNF。

在机器证明中,我们有时会利用纯数学的条件(能到达某一值,etc)来替代人类证明。有时我们是无法通过人类验证为什么该方法对,只能是用数学代数的方法计算证明其的确正确。

模型检测

模型检测是验证程序性质的自动方法

仅靠程序测试只能验证某些情况下是否有某一性质,模型检查可以验证所有情况下是否有某一性质。

NuSMV(模型检测器)使用语言的一部分。

程序可以是非确定性的。

弹珠游戏,再一次

从一个弹珠开始,每次可以:

1. 加五个弹珠

2. 弹珠数量加倍

直到100个弹珠。

模型检测器需要回答的一般问题:这个程序是否能到达98、99、100个弹珠?

一般来说,我们需要:

* 用变量和变量值表示的状态

* 步骤,即状态到状态的转移

上述两个东西加起来叫转移系统。

* 初始状态,即程序开始时的一些状态

* 需要检测的性质

在弹珠游戏里,状态是弹珠数量,开始状态是弹珠数目为1,需要检测的性质为是否能到达98、99、100个弹珠(的状态)。

可以将转移系统画成图,节点是状态,边是步骤。

 

如何用这种语言表示?

首先声明变量:弹珠数量(假设叫a)

VAR

a : 1... 100;

最初只有一个弹珠

INIT

a=1

对于转移关系,为:

TRANS

next(a)

此时a是指a的旧值,next(a)是指a的新值。

首先,第一种转移是a=a+5,但仅当a<=95时才允许

case a<=95: next(a)=a+5;

TRUE: next(a)=a; esac

若a<=95,则next(a)=a+5,否则a保持不变。

第二种转移是a=a*2,同样仅当a<=50时才允许

case a<=50: next(a)=2*a;

TRUE: next(a)=a; esac

由于在模型中是两种情况,每次是选择其中之一进行,而在NuSMV中,或由竖线表示。

因此,最后TRANS要改写成:

TRANS

case a<=95: next(a)=a+5;

TRUE: next(a)=a; esac

|

case a<=50: next(a)=2*a;

TRUE: next(a)=a; esac

此外,程序开头要加入MODULE main,表示程序开始。

TRANS中的

case P: Q;

TRUE: R; esac

等价于$(P \rightarrow Q) \cap (\neg P \rightarrow R)$。

计算树逻辑

描述模型检查器要检查的系统的性质的东西。

状态空间是所有状态变量的叉积(注意不是和):

$S= V_1 \times V_2 ... V_n$

如果我们从s到s'有一步可达的转移(转移关系/转移式子),那么我们写为$s \rightarrow s'$。

我们有一般假设:对于任意$s \in S$,有$s' \in S$使得$s \rightarrow s'$。

如果有确实没有的,我们给其一个指向自身的箭头使得其满足性质。

路径是一个无穷序列,包含若干状态变量,其中有$s_i \rightarrow s_{i+1}$。

CTL的基本思想:将系统的性质描述为是否路径的性质。

比如,满足$\phi$属性的状态可达等价于存在路径

$s_1 \rightarrow s_2 \rightarrow ...$

其中存在i使得$s_i$有性质$\phi$。

CTL中有预先建构的代码块E(存在)和A(全部),以描述路径中的变量的存在性。此外还有X(next,下一个变量),F(未来),G(全局,路径上每一个都满足)和U(直到)。

$\phi$可达:$EF\phi$(存在一个路径,路径上未来的某一处,$\phi$成立)。

CTL语法

原子命题:含有变量和=,≤,≥的命题。

真命题,假命题和原子命题均是CTL公式。

如果$\phi$和$\psi$是CTL公式,那么

$\neg \phi$、$\phi \cup \psi$……都是CTL公式。

$EX\phi$、$AX\phi$都是CTL公式。

$EF\phi$、$AF\phi$都是CTL公式。

$EG\phi$、$AG\phi$都是CTL公式。

$E\[\phi\U\psi]$、$A\[\phi\U\psi]$都是CTL公式。

CTL语义

假、真、原子命题、逻辑连接符均是其通常含义。

$EX\phi$意为,存在路径使得$s_2$满足$\phi$。

$EF\phi$意为,存在路径使得某个$s_i$满足$\phi$。

$EG\phi$意为,存在路径使得全部$s_i$满足$\phi$。

$E\[\phi\U\psi]$,存在路径使得某个$s_i$满足$\psi$,且在此之前的全部j(j<i),有满足$\phi$。

对于$AX, AF, AG, AU$,定义完全相同,只不过变成“全部路径使得……”。

例子:

 

 $EG blue$

 

附录

$\vdash$和$\vDash$

$\vdash$是句法后承,也就是说通过句法证明(比如$p \cup p \rightarrow p$)在原公式集里得到一个新的公式。

$\vDash$是语义后承,也就是说通过给符号两面的数值同有的字赋值,赋值相同时得到的真值相同。

可靠性(soundness)和完备性(completeness)

可靠性是指,如果有$\Sigma \vdash A$,那么有$\Sigma \vDash A$(被证明的结论就是有效的结论)。

完备性是指,如果有$\Sigma \vDash A$,那么有$\Sigma \vdash A$(有效的结论一定可以通过公式集得到证明)。

整数规划是NP-complete/NP-hard的

证明思路:证明CNF在线性时间内可以转化为整数规划

设有CNF,其中C包含$x_1 ... x_m$。

设有ILP,其中$x_1 ... x_m, 0 \leq x_i \leq 1$

对于C而言,$x_1 \cup \neg x_2 \cup x_3$等价于$x_1+(\neg x_2)+x_3 \geq 1$,而$\neg x_2$等价于ILP里的$1-x_2$

故任意CNF可在线性时间内转化为ILP,故ILP为NP完全问题。

posted @ 2024-07-08 02:25  redwind  阅读(14)  评论(0编辑  收藏  举报