自动推理笔记

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

命题公式

由若干布尔变量和运算符(¬,,,,)得到的公式

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

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

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

 

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

 

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

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

方法2:可满足式

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

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

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

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

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

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

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

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

Cn=j=1n+1(i=1nPij)

Rn=i=1...n,1j<kn+1(¬Pij¬Pik)

PFn=CnRn

 

Same for disjunction

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

 

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

所以至少有n+1项为真

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

PFn要求CnRn都为真,这是不可能的。

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

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

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

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

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

模型验证:

 

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

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

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

模型检测:

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

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

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

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

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

SAT的解法:Z3、Yices

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

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

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

SMT-LIB语法

 

 

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

 

 

SMT可以转化成SAT解决

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

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

and

等式逻辑

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

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

Computation Tree Logic,计算树逻辑

首先是命题逻辑

时间复杂性和空间复杂性

magnitude,规模

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

注意Ω是指式子大于等于

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表示,让求解器自己来解答

pij表示这个位置是否有皇后

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

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

至少:pi1pi2...pin

至多(对每一对而言,至多有一个为真):i=1...n,1j<kn+1(¬pij¬pik)

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

j=1n(i=1npij)

i=1n1j<kn+1(¬pij¬pik)

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

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

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

0<i<in(j,j:i+j=i+jij=ij¬pi,j¬pij)

八皇后结果之一:

(
(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里只有布尔变量,所以我们应该用二进制表示一个十进制数

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

注意这里an是指最后一位

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

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

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

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

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

乘法

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

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

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

 

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

从左到右处理b

 

 可以用循环不变式证明

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

比如A=7=111(2),A=(1,1,1)

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

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

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

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

引入ri,sij表示r和s在i步后的值(允许i=0,此时为0)

在第n步后的值就是rn

 

mul(a,b,rn)=j=1n¬r0ji=0n1(dup(ri,si)(bi+1add(a,si,ri+1))(¬bi+1j=1n(ri+1,jsi,j)))

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

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

 

 

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

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

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

SMT的一些应用

矩形覆盖

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

给小矩形编号,wi表示第i个矩形的宽,hi表示第i个矩形的高,以及矩形左下角点(xi,yi)的坐标

 

一个4*6的矩形

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

那么每一个矩形都应该在大矩形里,所以xi>0xi+wiWyi>0yi+hiH

矩形ij重叠的条件是,不保证xi,yi,xj,yj的具体情况,xi+wi>xj(第一个矩形的右侧边在第二个矩形的左侧边的右侧)且xi<xj+wj第一个矩形的左侧边在第二个矩形右侧边的左侧);且yi+hi>yj(第一个矩形上侧边在第二个矩形下侧边的上侧)且yi<yj+hj(第一个矩形的下侧边在第二个矩形上侧边的下侧)

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

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

如果可满足,会输出xi,yi,wi,hi

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

对于一行里只能出现一个的情况,我们依然说两两不同,此外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[i1]+1)(M[i]=2M[i1]),i=1...k

整个公式就是M[0]=1M[k]=1000i=1k((M[i]=M[i1]+1)(M[i]=2M[i1]))

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

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

再一次的,二分查找

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

步骤,指状态的改变

步骤+状态=迁移系统

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

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

 

可以用状态图表示

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

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

如果我们想要知道s是否可达,检查G¬ϕ,如果成立,那么¬ϕ全域成立

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

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

同时,描述初始值的要求,v0。描述最终值的限制,vk

同时对于时间序上的每一步,描述vi如何通过vi1表示。

特别的,当值不变时,特意使值保持不变vi=vi1

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

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

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

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

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

P:True

S:用aij表示j次迭代后a[i]的值,有 aj+1,jaj,j1i=1,ijmai,jai,j1 (整个式子就是j=1m1(aj+1,jaj,j1i=1,ijmai,jai,j1)

Q:am,m1a1,m1

归结

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

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

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

i(jlij),其中lij是文字。

具体例子(pq)(¬p¬q)(p¬q)(¬pq)

此为不可满足式

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

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

如何通过CNF检查SAT?

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

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

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

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

我们只用到归结方法:如果CNF中有pV¬pW,可以自动得到新析取式子VW

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

pV,¬pWVW

子句中,顺序并不重要

此外,pp=p

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

例子:

 

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

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

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

形如pq¬p¬q的式子可以归结,但没什么用,因为归结后会产生恒真式

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

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

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

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

证明某个公式包含另一公式,比如pq,即证明p¬q不可满足

即,如果这个公式恒真,由于pq等价于¬pq,那么式子等价于证明¬pq恒真,即p¬q不可满足。

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

 

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

如果式子不是CNF,转换:

 

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

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

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

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

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

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

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

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

 

 

 

如果X为空,那么可满足

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

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

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

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

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

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

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

需要证明X{p}⊢⊥X{¬p}⊢⊥,这样才能证明X⊢⊥

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

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

对于¬p也是一样的

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

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

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

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

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

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

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

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

M扩展:

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

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

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

如果l在M中出现过,我们说Ml

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

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

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

MMl,如果l还未在M出现过且M¬C

 

对于Decide操作,有:

 

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

 

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

如果M¬C,且C在CNF中,那么Mfail

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

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

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

扩展阅读

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

 

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

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

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

任意命题句转CNF

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

转换成CNF。

真值表法

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

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

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

Tseitin变换

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

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

对于每个子公式Ψ,若其只包含一个文字,则其命名nΨ=Ψ,否则,nΨ为一新文字,指代原式Ψ

例:

Ψ=¬q,nΨ=¬q。此时等于没有变化。

Ψ=pq¬r,nΨ=A。相当于我们引入了一个新变量代表Ψ

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

nΦ

CNF(q¬nΨ),其中q是¬nΨ的命名

CNF(qnΨ1nΨ2),其中q是nΨ1nΨ2的命名,而是四种逻辑运算符之一。

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

(¬sp)B((qr)D¬p)C

这个公式Φ经过变换得到:

nΦ

CNF(nΦ(BC))(此处为对Φ规则3)

CNF(B(¬sp))(此处为对B用规则3,注意上个子句里已有B出现,但仍需对其子式进行CNF)

CNF(C(D¬p))(同样,对C用得到D,所以仍需再用)

CNF(D(qr))

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

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

nΦ为真(因为这和原式子的值相同)

q¬nΨ为真,因为q是后者的命名,如果后者取假,前者会取真,整个式子为真,反过来如果后者取真,前者会取假,整个式子仍为真。因此其CNF也一定为真。

同样,根据定义,此时qnΨ1nΨ2为真。同样,其CNF也一定为真。

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

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

由于T(Φ)会满足CNF(qnΨ1nΨ2),那么也一定满足qnΨ1nΨ2(对¬的情况也同理),因此qnΨ1nΨ2会为真。根据定义,当这个条件满足时,q(nΨ)的值是Ψ的值。

nΦΦ的值,所以Φ此时为真。

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

一个方便的转换:

CNF(p¬q)=(pq)(¬p¬q)

CNF(p(qr))=(p¬q¬r)(¬pq)(¬pr)

CNF(p(qr))=(¬pqr)(p¬q)(p¬r)

CNF(p(qr))=(pqr)(p¬q¬r)(¬pq¬r)(¬p¬qr)

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

不只是CNF,甚至是3CNF。

单纯形法

处理线性不等式。

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

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

在实数x1,x2,...xn0的问题中,我们想要对目标线性函数v+c1x1+c2x2+...+cnxn找到一个最大值,使其满足k个限制ai1x1+ai2x2+...bi(其中ci,bi,aij为给定实数,且有bi0

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

变体转化

一些将变体转化的方法:

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

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

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

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

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

松弛形式

由于处理等式比处理不等式方便,将原本的约束ai1x1+ai2x2...bi转化为yi=bi(ai1x1+ai2x2...),同时引入一个新的不等式yi0

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

在松弛形式中,若yi=bi,即xj=0,称此为基础解。

此时,所有yi为基变量,所有xj为非基变量。

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

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

最大化z=3+x1+x3

满足:

x1+x2x32

x1+x33

2x1x24

转化为松弛形式,有:

y1=2+x1x2+x3

y2=3x1x3

y3=42x1+x2

y1,y2,y30

基本解为:x1=0,x2=0,x3=0,y1=2,y2=3,y3=4

选择原式中增加幅度最小的变量,在这里x1x3增加幅度一样,故选择x1。对于x1,由于我们要保证yi0,因此三个式子对x1的取值限制分别为无限制、x13x12,故使x_1取值2,并与y3式子互换。

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

x1=212y3+12x2

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

z=512y3+12x2+x3

y1=412y312x2+x3

y2=112x2x3+12y3

x1=212y3+12x2

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

接下来,我们选择z中增幅最小的x2做转轴。三个式子对其取值约束分别为x28x22、无限制,故选择y2x2式子互换,将第二个式子转换:

x2=22y2+y32x3

代入,有:

z=6y2

x2=22y2+y32x3

y1=3+y2y3+2x3

x1=3y2x3

现在,z的值不可能比6更大,因为y2最小取值为0。此时基础解正好得到z=6(其中x3,y2,y3=0,x2=2,y1=3,x1=3,z=6

更一般的,对于求解式子z=v+j=1ncjxj最大值,满足不等式组yi=bi+j=1naijxjbi0的问题,只要z中还存在正项cj,我们就对正项的非基础变量找到限制其最严重的式子,并做转轴操作,同时维持松弛形式,直到不存在正项为止。

一些注意事项:

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

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

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

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

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

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

对一组不等式ai1x1+...bi(其中并非所有bi0)。

方法:

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

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

此时,我们有定理:

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

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

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

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

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

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

x3y12

x+y10

x+y7

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

x3yz12

x+yz10

x+yz7

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

y1=12+x+3y+z

y2=10xy+z

y3=7+xy+z

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

得到z=12+x+3yy1

且有:

z=12x3y+y1

y2=222x4y+y1

y3=54y+y1

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

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

x=1112y22y+12y1

z=112y2+y12y1

z=1+12y2y+12y1

y3=54y+y1

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

z=z

x=932y212y1+2z

y=1+12+12y1z

y3=1y12y2+4z

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

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

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

由于给出的线性规划问题都是凸空间,且目标函数是线性的,所以局部最优值就是最优解(算法导论第三版p. 498)。p500的对应关系应该主要说的是不一定有(尽管大部分时候有),比如替换非负的时候需要引入新变量。

在SMT中使用单纯形

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

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

在CDCL中,我们常常会有M¬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

等价于(PQ)(¬PR)

计算树逻辑

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

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

S=V1×V2...Vn

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

我们有一般假设:对于任意sS,有sS使得ss

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

路径是一个无穷序列,包含若干状态变量,其中有sisi+1

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

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

s1s2...

其中存在i使得si有性质ϕ

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

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

CTL语法

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

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

如果ϕψ是CTL公式,那么

¬ϕϕψ……都是CTL公式。

EXϕAXϕ都是CTL公式。

EFϕAFϕ都是CTL公式。

EGϕAGϕ都是CTL公式。

E[ϕUψ]A[ϕUψ]都是CTL公式。

CTL语义

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

EXϕ意为,存在路径使得s2满足ϕ

EFϕ意为,存在路径使得某个si满足ϕ

EGϕ意为,存在路径使得全部si满足ϕ

E[ϕUψ],存在路径使得某个si满足ψ,且在此之前的全部j(j<i),有满足ϕ

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

例子:

 

 EGblue

 AFblue

有一定的冗余度:

 (最后这个的语义需要查

通过原子命题、EX、EG、EU即可生成全部CTL式。

状态被原子命题集表示的转移系统称为克里普克(Kripke)系统,这能给CTL提供定义框架。

注意AE必须和后面的XGUF一块出现。

验证性质

验证某个系统是否有某个性质是指,已有转移系统(S,→),求对于任一起始状态sI而言,是否满足ϕ。注意,有时会省略,但我们一般就是要检查从起始集合开始的路径。

基本思想:计算Sϕ,为满足ϕ的所有状态s。其中s满足φ意味着从s起始的所有路径满足φ(注意,这里没有限制s是起始状态,此外在本节中,我们用‘成立’代表某个具体的状态上有某个性质)(我现在理解的是,EFx的问题是从s起始的所有路径中,exists一条路径如何如何)。

问题转化为验证要算的起始状态集合I,I是否是Sϕ的子集。

由于ϕ由CTL元素构成,需要描述怎么计算对应集合。

首先,Sfalse=Strue=S

对于原子命题,Sp={sS|p(s)}(实际上是要求s0满足p(s))。

S¬ϕ={sS|sSϕ}

Sϕψ=SϕSψ

Sϕψ=SϕSψ

最简单的是EXφ,有SEXϕ={sS|tSϕ:st},即s属于S当其某个路径上的下一个状态φ成立。

对于EG和EU,由于其包含可能无穷多个状态,我们需要其他的方法:

总体思路:考虑前n步,直到前n步内对应集合不再改变,即为找到对应解。

EGϕ是指,存在路径s0s1...,其中每个si都有φ成立。

Tns0的集合,使得对于这些s0s0s1...sn上都有φ成立。

T0=Sϕ,对于Tn+1,有:

Tn+1=Tn{sSϕ|tTn:st}

故验证EGϕ可写成算法:

T0=Sϕ,n=0

repeat:

Tn+1=Tn{sSϕ|tTn:st}

until Tn+1=Tn 

这一算法结束时,如果对于>某一特定值n的i而言,都有Tn=Ti,这也就是整个算法得到的解(如果存在),SEGϕ=Tn

由于状态只有有限个,所以如果存在,那么就说明EGφ成立。

由于有repeat until,考虑是否会出现不终止的情况,但由于状态数是有限多的,且每次∩操作会让T变小(或不变,此时算法终止),故算法一定会终止。

E[ϕUψ]意为存在n使得Pn成立:

Pn成立当且仅当存在一条路径s0s1...,其中ψsn成立,而ϕsi(i<n)上成立。

同时,令UnPi成立的s0状态集,且in

U0=Sψ

Un+1=Un{sSϕ|tUn:st}

同样,有如下算法:

U0=Sψ,n=0

repeat:

Tn+1=Un{sSϕ|tUn:st}

until Un+1=Un 

同样,对于>某一特定值n的i而言,都有Un=Ui,这也就是整个算法得到的解(如果存在),SE[ϕUψ]=Un

同样,由于有repeat until,考虑是否会出现不终止的情况,但由于状态数是有限多的,且每次∪操作会让U变小(或不变,此时算法终止),故算法一定会终止。

如果我们用显式的状态表示集合——直接表示每个状态,整个算法的复杂度是V的,但是如果用BDD法,可以解决更大的问题。

例子:

弹珠问题:是否能从a=1(初始弹珠个数)到达a=98。EF(a=98)转化为EX,EG或EU:

E[trueU(a=98)]

ϕ=true,ψ=(a=98)

U0=Sψ,n=0

repeat:

Tn+1=Un{sSϕ|tUn:st}

until Un+1=Un 

U0={98}

U1={49,93,98}

U2={44,49,88,93,98}

...

U18=U19,且此时有1。

于是SE[trueU(a=98)]=U18

NuSMV全文:

MODULE main

VAR

a: 0... 100;

INIT

a = 1

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

CLTSPEC EF (a=98) (CTLSPEC !EF (a=98)用于检查具体的例子)

显式模型表示验证与符号模型验证

需要表示状态的集合以快速完成操作,比如交并补以及步骤集合。

在显式模型验证中用显式表示法,这些集合只是用枚举元素的方法表示,容易爆上限(只需要大概5个1-100的变量,整个集合的大小就可以达到10^10)。

在NuSMV中有二进制和数字,但是我们可以用二进制表示数字。

我们之前已经知道加法和乘法可以用二进制算数式(add, mul, etc)表示,而原子命题也可以用二进制算术式表示:

于是,符号模型验证。使用布尔值计算和表示:

对于一个由若干二进制串组成的集合S,S=Bn,可以定义子集U,布尔函数f_{U}=(0, 1),其中如果sU,则f_{U}=1。

我们希望能有一个模型能将每个布尔函数独一无二的表示(对于式子表示法不可能,因为p∩q=q∩p=……),且可以快速计算相关集合的交并补(对于显式表达同样无法做到,由于现在使用了二进制表达一切,比如false代表空集的话,其补集就是全集,计算全集非常慢),还有部分高效的布尔函数表达法。

假设一个布尔函数负责计算n个变量的交并补,那么这个布尔函数有2n个定义域,对于定义域上的每个值要么得到0要么得到1。显然,两个布尔函数不同当且仅当二者至少有一个值得到的结果不同,也就是说一共有22n个不同的布尔函数。同时,如果我们需要将这些函数都以不同的形式表达出来,我们需要2n个位来表示。

故,我们希望在实践中常用的布尔函数能有高效的表达。

二元决策图

二元决策图(Binary Decision Diagram),BDD。

数据结构,满足上述所有要求。BDD基于决策树(这里实际上是二进制版的决策树)。

二进制决策树每个节点上有一个布尔变量,叶子上有一个布尔值。

 

有点类似trie树,函数值如此计算:

如果要求的定义域中的某个布尔值为真,进入左子树,否则,进入右子树。整个函数的真值就是叶子上的真值。

一切布尔函数都可以有决策树的表示,因为一切布尔函数可以以真值表表示,而真值表可以用有2n个叶子的决策树表示。

一切布尔函数都有独一无二的表示法吗?不,可以很容易的构建两颗功能一样,变量顺序不同的树。 

可以通过修正顺序来解决变量顺序的问题,定义p<q意为q的顺序小于p。修正后的二进制决策树有对于上方的节点n和下方的节点n',总有label(n)<label(n')。

 现在,我们依然不能保证整个树是unique的,比如我们可以做一棵树T,假设p是未出现在T中的最小值,我们可以堆叠p来破坏uniqueness。比如,p,左子树T,右子树T;与T等价,但并不一样。

对于这个例子,我们显然希望要较小的树T,因此定义用T取代T'的操作为消除。

我们称呼不可再进行消除的树T为reduced ordered decision tree。

对于RODT来说,每个布尔函数都有独一无二的表示。

存在性:任意布尔函数可以被树表示

首先从有序树开始,一直做消除操作,每次消除操作严格减小树的大小,但是树中的有序性不变,最后由于原本的树能够表达布尔函数,有序树也能表达布尔函数。

 

 注意即便是叶子也可以做消除。

任意布尔函数只会有一颗

存在性显然:通过构造+消除,一定能得到ROA树。

唯一性:假设T和U是两颗ROA树,假设其表示的函数一样,那么T=U。

归纳,假设存在0个变量,那么函数要么0要么1,显然T=U。

假设存在n个变量(n>1),T和U有同样的根p。此时,如果我们只看p为真的情况,那么T1和U1表示的函数相同,且T1=U1(因为这时存在n-1个变量,而根据归纳法n-1个变量已经被证明表示函数相同时两颗树相同),对p为假的情况也同样。

而同加p等于仍相同。

接着假设T和U不同根,假设T的根是p,是最小的变量,由于有序性,如果p不出现在最顶,但又小于所有其他变量,那么p不能出现。

由于T和U表示的函数一样,如果p为真,T结果为T1,U结果为U,同样对p为假时T2也和U相等。因此,我们得到T1=T2=U,因此可以再做一次消除。所以不可能存在不同根的情况。

BDD是高效储存的决策树。具体:如果一个子树出现过两遍,只存一遍。

 

现在不再是树,而是DAG。

对树而言,我们存了每个节点和其儿子的指针,每个节点只有一个指针传入。对于DAG,唯一的区别在于每个节点可能有多个指针传入。

BDD只是DAG表示的决策树。这也就适用于ordered BDD。由于我们希望得到一颗尽量高效的树,我们不仅要share,还要尽可能多的share。怎么做?现在定义左儿子指针叫真后继,右儿子指针的东西叫做假后继。

我们现在要求,DAG中不得出现以下三种情况同时满足的两个节点:

两个变量标记一样的节点;

两个节点真后继一样;

两个节点假后继一样;

如果发生,可以合并:注意如果合并后某个节点左子树和右子树一样,那么可以移除该节点。

 

ROBDD是指不可能进行合并和消除的有序BDD。

假设变量顺序已定,对于每个布尔函数,其正好有一个ROBDD表示。此外,在考虑ROBDD时,也可以认为真值表的顺序和ROBDD的顺序一致。

同样证明很简单:

存在性:

对于R有序树,我们尽可能地做合并和消除操作,最后就会得到一颗ROBDD。对于ROAT有表示,那么对于ROBD也存在表示。

唯一性:

证明如果有两个ROBDD表示同一个布尔函数,那么他们实际上是同一个。

我们将两个ROBDD展开,能得到两个ROAT,ROAT一样,然后做merge and eliminate,可以证明这个过程唯一,故其唯一。

ROBDD的一个子节点的树也是一个函数。而且,其中没有任意两个子节点的树相同,因为如果相同,那么可以合并。

ROBDD对2SAT而言需要选择好顺序,不然可能会得到指数爆炸的ROBDD。一种优化法:每次选择formula中临近的变量定序,以此能保证ROBDD也尽量小。

如何计算ROBDD?从树开始转化当然可以,但是太麻烦。

任何公式实际上包含:1. true or false 2. 变量p 3. 否定 4. 二元运算符。

应当递归的构筑ROBDD:

首先,ROBDD(false)=0,ROBDD(true)=1。ROBDD(p)就是最基础的ROBDD。

ROBDD(¬phi)= 1. 10符号互换 2. ROBDD(ϕfalse)。

所以最重要的就是,如果我们有ϕψ两个公式,怎么计算二者运算后的ROBDD。

计算法:首先计算ROBDD(ϕ)和ROBDD(ψ),之后递归的计算ROBDD(ϕψ)。这也是这里的核心所在。

找到一个算法,收两个ROBDD和一个二元运算符,求出之后的ROBDD。

p(T, U)代表p的左子树为T,右子树为U。

此外,我们会用(T,U)代替算法ROBDD(T,U,)。

作为递归的最底层,有(T,U)如果T,U值为{0, 1}。

接着考虑非最底层的情况,假设p是其中出现的最小变量,有可能:

* p同时出现在T和U两个公式里。此时,p肯定同时出现在T和U的最上方。

(p(T1,T2),p(U1,U2))=p((T1,U1),(T2,U2))

* p只出现在T中,没出现在U中。p这时在T的最上方。

(p(T1,T2),U)=p((T1,U),(T2,U))

* p只出现在U中,同样,p一定在U的最上方。

(T,p(U1,U2))=p((T,U1),(T,U2))

一种记法:p为真时都向左,p为假时都向右。

先全部替换,然后自上而下的算子树的ROBDD,过程中推向下(见最下方例子,这里每次会把大的提上来)。

复杂度:

如果从T到T'有k条路经,U到U'有n条路经,那么计算一个运算符会总共计算这两个节点的ROBDD kn次。

解决方法:哈希表

关键:每对同层节点只计算一次。

复杂度O(size(ROBDD(T))*size(ROBDD(U)))=n2

是否结果总是reduced?不!

所以要做消除和合并。对每个新节点都要检查能否做消除和合并操作。

O(size(ROBDD(T))*size(ROBDD(U)))=n2

例子:

(pr)(q(rp)),顺序p<q<r。

注意,起始时树长成下边这个样。

 对于左子树:

 对于右子树:

 

 最后将∩推下去。

 

用ROBDD做CTL:

EG、EX、EU的核心是集合的交并补和一步走,聚焦于这些操作:

首先,可以考虑集合中存在某个元素为真,不存在为假。由此,可以将状态写成ROBDD。

交和并与ROBDD中的合取和析取相关。

唯一缺失:一步走需要ROBDD表示

如何做?将每个数字转化成二进制。假设得到ai,现在要求下一个ai

ai转移到next(a),而根据上面的,这两个集合都可以用ROBDD表示。

 对于P而言,第一部分是蕴含,可以用ROBDD表示,第二部分在定序ai<aj<ai<aj后,将U中所有的ai替换成ai即为结果。

两部分合取,得到ROBDD。

 可以消除存在量词。

狐狸与兔子问题:

一步=从一侧到另一侧

 b是船在哪一侧,f是指在船这一侧的狐狸数量,r是指在船这一侧的兔子数量。

另一侧可以用3-f,3-r算出来。

 没有next(fb)或者next(rb),没有连续关系。

 主要是因为题目保证了最多两只,所以一定不会船上被吃

不然就得加限制了。

检测网络中的死锁:

无法通过规则发生变化的状态。

在转移系统中,称:

如果从s无法到达与s不同的状态s',称s进入死锁状态。

我们希望不会到达死锁状态。

网络是有向图,有向图称作通道,通道最多可以过一条信息。

这个网络里一共有三种操作:发送、接受和转发

 注意这里还有一个条件,就是信息要随时在一个通道内。只有对应的状态可以处理信息。

初始状态:所有通道为空。

异步计算,任何时间都可以发送信息。

每个通道c,定义域为0……n(0为空,否则为目的地编号)。

由于有信息永远在通道中打转的可能,定义

OK(n, m, c)表示n到m的路径走了c通道是否可以,如果走的最短路,那么可以。

三种转移:

发送新信息:选择一个空的通道替代m。

 接收:如果现在边的终点是m,且目的地也是m,直接将当前通道用0替换。

 最后传递:

整个系统的转移关系只是以上三个操作各种可能性的析取(全部)。

最后整个系统:

死锁的情况就是所有东西都不变了,也就是所有transition中能做的步骤(Q)都不能做,所以是!Q。

变体:只有若干个点被允许发送信息和接收信息,假设其集合为M。

 这样的序列是允许的。

局部死锁:某一部分永远不变。

 

有界模型检查:

步数总数有限(有界)且固定(What's the fixed for?)

CTL不能用,需要用LTL。

可以用SAT,也可以用NuSMV -bmc。

NUXMV新版本

BDD还能用来解决伪布尔约束,即线性规划的一种,但其形式为cixia,其中xi为0或1。其他值为整数。

这种式子实际上也是要么可行,要么不可行。

这东西可以通过BDD转为CNF。首先是启发式方法,选择参数最小的x在BDD的上层。实际上是启发式搜索构建BDD

 现在有BDD了,怎么做CNF?

 考虑每个节点是if-then-else,整个树可以化作:

 对每个节点引入一个新变量,就可以将整个树化成CNF了。

Tseitin转换后的一些性质:

1. 有一个单位元,名字为整个树的根

2. 对于每一个节点,实际上长成:

 其中,A是该节点的新名字,a是该节点本身的名字。(为什么重新命名的是整体,而非每一小部分?是因为现在需要用一个新变量代表一整个树吗?

谓词逻辑

到目前为止我们只考虑了定义域在$\{true, false}$上的情况,现在我们要考虑更广泛的情况。

我们希望在任意定义域上做,我们有量词,,我们有关系和函数。

 

 证明结论是否命题是不可满足的。我们需要先定义什么是可满足和不可满足。

首先我们先看看怎么表达各个属性:S(x)(x是一个学生),L(x)(x是一门课),B(x)(x很无聊),A(x, y)(x在课y期间醒着)。

这里这几个东西是关系/函数,以true,false为结果。

改写原文后,我们有:

x(S(x)y(L(y)A(x,y)))x((L(x)B(x))¬y(S(y)A(y,x)))x(L(x)B(x))

其中:

x(S(x)y(L(y)A(x,y)))是有一个学生在所有课都醒着。(x是一个学生,且对于任意课y,他醒着)

x((L(x)B(x))¬y(S(y)A(y,x)))是如果有无聊课,那么一定无学生醒。

x(L(x)B(x))是结论的否定,即至少有一门课无聊。

在这里我们引入了一些新东西:

1. 变量(x,y)

2. 函数标识(这里没有,是定义域值域任取的函数)

3. 关系标识(S,L,B,A),也叫做谓词标识。

正式定义:

一个term定义为:

1. 一个来自定义域上的变量,或者

2. 一个有n个term的函数符号

谓词公式的定义为:

 如果我们想要谓词逻辑为真,就要给谓词逻辑赋值使其为真。

我们给予谓词逻辑的定义域的全体称为模型。在样例里这个模型包含所有学生和课程。

定义α是一个替换,将变量替换成模型中的值,其中有:

[x,α]=α(x),对于xX

[f(t1,...tn),α]=[f]([t1,α],...,[tn,α])。其中t是一组term。

[R(t1,...tn),α]=[R]([t1,α],...,[tn,α])。其中t是一组term。

这里只是语义。

有了这个,我们就可以给出的形式化定义了:

对于而言,我们知道P在任意时候都成立,所以我们就要求将x替换成任意定义域上的值,这就可形式化表示成:

α:XM,xXmM,那么我们定义αx:=m:XM为:

αx:=m(x)=m

αx:=m(y)=α(y)

其中对于所有yX,yx

进一步定义,为:

[x(P),α]=mM[P,αx:=m]

[x(P),α]=mM[P,αx:=m]

最后是[,α]=[̸P,α][PQ,α]=[P,α][Q,α](其中,,,)。

由上面这一大串我们正式定义了[P,α]{true,false}的语义,我们说P是可满足的当且仅当存在模型M使得[P,α]为真。

命题逻辑可以被视作谓词逻辑的特殊情况,其中无变量,无函数符号且每个关系符号有0个变元(即P的真值不取决于变元)。

SMT做无限域上的谓词逻辑很弱,尽量避免。

扩展resolution到谓词逻辑上

由于resolution只适用于CNF,我们需要定义在谓词逻辑里什么是CNF。

在我们之前的谓词逻辑里,CNF是若干clause的交,每个clause是若干literal的并,每个literal要么是一个命题变元,要么是命题变元的否定。

现在,literal可以是一个任意数量变量的关系符号。

而clause现在就是任意literal,注意这也适用于多个literal的情况(每个literal都是全称量词)。

虽然说变得很多,但是CNF还是只能用来直接证明式子是不满足式(通过得到空子句,其他方法则需要转换)。

对于resolution而言,基础的东西还是一样,假设我们有原子公式P,有PVW,那么有VW

但是,我们会有一些特殊的情况,比如说:

 

 

而这里依然是合法的resolution。(因为范围变窄了,不过新得到的式子可能没那么“好用”)

解法:代入(substitution)

将所有变量转为term。除了要换变量外,也要换函数内的变量。

允许多次替换term。

 

 

 

 复合时注意是后序

现在,这里的CNF长这样:

如果我们有PVW,且Pσ=Qτ,那么我们能通过resolution得到VσWτ

于是我们现在需要一个算法确定能否有Pσ=Qτ,而如果有,那么需要输出结果。(合一化问题)

我们一般对P,Q都是关系符号的情况感兴趣,但是我们也可以给其他的做合一化。

注意:如果P和Q并非同一种符号,那么一定不能合一化。

如果最外层的某个函数一开始就不一样(且不是变量对应),那么一定不能合一化。

最后是如果一开始有一个变量大,那么一定不能合一化

 (对应上面四种情况)

如果我们能够通过重命名使得两侧没有任何一个变量相同,那么我们也可以让τσ“合成”一个操作(当然等价)。

所以,一般的合一化问题为:

给两个termP和Q,能否找到一个代入τ使得Pτ=Qτ?如果可以,找到它。

我们称这里的tau为unifier(合一符)

合一符的性质:

如果σ是(P, Q)的合一符,而τ是任意代换,那么στ也是(P, Q)的合一符。因此,只要我们找到一个合一符,就能找到许多合一符。

因此,我们定义最一般合一符σ0为对于(P, Q),任意合一符σ=σ0τ(其中σ为一代换)。

存在性:通过算法中的证明可以得到

唯一性:假设存在两个不同的合一符,则有τ1=τ0σ0,且τ0=τ1σ1,凭此我们可以证明合一符只会在重命名时发生不同。

v(t):如果t是变量,返回真,否则返回假。

in(x, t):如果变量x在t中出现,返回真,否则返回假。

有以下不变式:

σ(Pσ=Qσ)σ((t,u)S(tσ=uσ))(σ(Pσ=Qσ)un)

其中S是一个集合,包含了分解(P, Q)得到的对。

如果我们找到一个理据使得(P, Q)不能合一,那么un为假,否则为真。

(我的理解:这个不变式实际上是每一轮结束后和开始前都满足,但过程中不一定处处满足,这里明天看看有空没再问一下)

现在我们正式给出这个算法:

 

(情况1对应上面的第四种情况

情况2强制替换t为U

情况3类似情况1

情况4类似情况2

情况5,两边都不是变量,且都有某个函数f,那么其每一项在变换后都必须一致,因此将所有子项对加入S

情况6,两边函数不一样,那么没解)

此算法一定会终止,因为每一步要么减少S的大小,要么减少S中变量的数目。

算法终止时,如果un为真,那么有解,否则无解。

引入mgu,为整个式子的最一般合一子。mgu仅在发生替换时才会更新。而且,分解时mgu也不变。

注意:

1. 不考虑重命名的情况下,mgu唯一

2. occur check可能很耗费时间

注意最终结果可能是2n的,但类似BDD,用合并等操作,可以优化到线性。

工具:prover 9

从现在起,我们只允许在resolution中使用mgu,而非其他的合一子。因为如果只允许mgu,那么就能找到一个算法找出所有单步的resolution。

所以,从这里我们可以得到谓词逻辑下的resolution。

找CNF所有可能单步resolution:

1. 挑两个不一样且非空的子句

2. 重命名使得二者没有变量名字一样

3. 选择一正一负两个literal P和¬Q

4. Unify二者

5. 如果可以unify,得到VWσ,如果不可,不能resolution。

谓词逻辑中的resolution也满足完备性:一个命题为假当且仅当能够得到其为假

然而,resolution此处还有不可确定性:不能导出所有为假的CNF,这与之前的命题逻辑不一样。这里的情况与停机问题类似:如果你能导出一个命题为假,那么其肯定为假,但是你可能不能导出一个假命题。

由于现在方案数更多了,我们现在只允许最多含有一个正literal的子句参与运算,以降低工作量。这种子句叫做Horn子句。

Horn子句中:

唯一出现的正literal叫头(head)。

没有头的clause叫goal。

只有头得clause叫fact。

Horn子句用于prolog语言中,大概长这样

 都是图运算,arrow表示x到y有一条有向边,定义PATH是若干arrow。

这里要算什么,还是加什么的否定。

SLD逻辑是从结果的否定开始,试图得到一个空子句。

 

 

除了这些定义之外,其他的是由计算机自动处理的。

声明式编程。

现在我们来考虑对于任意的谓词逻辑,如何转化成CNF。到目前为止,我们只考虑了全称命题的CNF,现在考虑更一般的情况。

将一般的情况转化为之前的情况,包含两步转化:

1. prenex normal form:将所有不在最前方的量化词转移到最前方。

2. 斯科伦化(Skolemization):将所有存在量词转化为全称量词

1. 具体步骤:

1.1 移除所有

AB(AB)(BA)

AB(¬A)B

1.2 ¬只允许出现在单独的literal前

 

截至到这,就只有,,,,但是我们还要继续:

首先,将所有变量换名保证不重名。

假设现在定义域都非空,那么有

Bx(A)x(AB)

注意这里实际上不止适用于全称量词,也适用于存在量词,不过形式要稍微变一下。

1.3 disjunction中不能再出现conjunction

分配律和结合律,也可以用Tsetin转换。

可以证明上述步骤不会一直运行,总会停止

现在只剩下所有量词都在最前方的句子了,做斯科伦化

2. 斯科伦化

如果形式为

...y...(y)

那么这就等价于对于前面一些变量,存在一个函数y=f(x1,x2...xn)。这是因为如果对于x1...xn,都存在一个y,那么这就等价于都存在一个f(x)(虽然我们不知道具体是什么函数)。于是现在就没有存在量词了。

斯科伦化可以保证式子满足性不变。

 

 选择公理

新内容

使用树表示命题逻辑

虽然我们常见的算法是用公式表示命题逻辑,而实际上我们还可以用树表示命题逻辑。

命题公式可以通过树递归定义:

* 叶节点上的命题变元是命题公式

* 有¬符号的节点,且其含有一个儿子是命题公式,那么这个节点是命题公式

* 有二元运算符且有两个儿子,两个儿子都是命题公式。

命题公式串是通过对树进行中序遍历得到的。

两棵树都表示了pq¬p¬q。但是,这个字符串的运算顺序是不明确的,因此我们需要进一步定义运算顺序。注意,含有否定符号的节点的儿子视为右子树。

解决运算顺序的第一个方法是中序遍历过程中加入括号规定运算顺序。但这样比较复杂。

其二,是我们约定好运算符优先级:在这里,我们规定优先级为:¬>(&)>(&)>→>(&)。且同级运算符总是右结合的。

此外,一个方法是使用波兰表示法和逆波兰表示法,使用波兰表示法和逆波兰表示法通过先序遍历消除歧义,且可以用栈快速进行运算,但为了方便人,我们还是用中序遍历。

equasatisfiability:如果两个式子均是可满足的(或均是不可满足的),称他们为equasatisfiablity。

到目前为止没太大用处。因为可以视为逻辑等同。

结构归纳:我们假设A是一任意命题公式,若A不是原子公式,称在A对应树最上端的逻辑运算符号为主运算符。与数值上的归纳类似,我们先证明某个性质对叶子成立,再证明对于每一颗树,如果子树满足性质,通过主运算符我们可以证明整棵树也满足。

由此,证明属性对所有公式F成立,有:

1. 证明对任意原子命题成立。

2. 假设对命题A成立,那么对命题¬A也成立。

3. 假设对命题A和命题B成立,那么对命题AB也成立。

此外,我们还可以用生成语言来表示命题逻辑。

语法:句子的表示。

语义:句子的含义,比如a*b+2,就是带入两个值后及其得到的结果。

新引入的符号,与非,即除了A和B都是1时为0,其他结果都是1。,或非,即只有A和B都是0时为1,其他结果为0。

我们有时候会任意给出一组赋值,以证明两个公式逻辑相等。

一个令一个式子为真的赋值成为一个模型。

渐进

Θ是指整个函数上下界都渐进于某个函数,O是指某个函数上界渐进于某个函数,Ω是指某个函数下界渐进于某个函数。一般来说,我们总是讨论的对任意情况下的足够大的n。虽然我们平时是写f(n)=O(g(n)),但实际上有O(g(n))={f(n1),f(n2)...}这样。但我们常常会把O(n2)=kn2用,所以影响不太大。

归约:在多项式时间内,将一个未知问题A转化成一个已知问题B,并且已知问题B有多项式解法,即可证明A也有多项式时间算法。

我们常用的是反过来用,假设某个问题B能从一个NP问题A转化而来,假设找到了多项式时间的转化算法,但是由于A没有多项式时间算法,如果B有多项式时间算法,那么A就有了多项式时间算法,故B一定没有多项式时间算法。

判定语言:接受输入的串,且对于这些串中结果为1的串正确输出结果,结果为0的串正确输出结果,且不能有不停机的情况。

接受语言:接受输入的串并对这些串输出1(输出0的串称为拒绝)

可以通过构造的方法证明复杂类P也包含任何能被多项式时间算法接受的串。比如,首先,我们已经知道多项式时间内判定的串一定是多项式时间接受串的子集。如果我么能证明多项式时间接受串也是多项式时间判定串的子集,那么就能证明。这很简单:假设在cnk步后串被接受了,那么对于判定来说也接受了。否则,对于判定而言我们令其不被接受。

问题——对于图灵停机问题为什么不适用?

Clausal形式

Clausal形式中,有:

* 每个子句是一组文字,且隐含是其析取。

* 空子句(没有任何元素的子句)存在,为

* 整个东西是一个CNF。如果整个东西为空,用表示。

比如{{p, r},{¬q}}=

pr

q

任何CNF都可以转化成3CNF。

对于1CNF,引入两个新文字l1,l2,原CNF的一项可化成:

p=(pl1l2)(pl1¬l2)(p¬l1l2)(p¬l1¬l2)

对于2CNF,引入一个新文字。

对于k-CNF(k4),引入k3个新变量l1...lk3,原式子化为:

p1...pk=(p1p2l1)(¬l1p3l2)(¬l2p4l3)...(¬lk3pk1pk

证明:

用resolution,每次消去最后一项,有pV,¬pW=VW,不断合并,即可得到最初的式子。

由此也得到证明,引入新变量的值无关紧要:

对于1,2CNF而言,由于是通过分配律得到,所以显然两侧完全等价且两侧取值一样。

对于k-CNF(k≥3),由于resolution步骤是可靠且完备的,故每次resolution前后等价,故取值一样时结果一样。

证毕。

蕴含图

 

 0/A=t表示在第几层递归树上,该变量的值被确定为t/f。

边的意思是通过第几个语句确定。

3/{}代表第几层处导出了矛盾。

 

切:通过切将蕴含图分成两块,其中切将根节点(决策变量,包含中间的变量)和叶子节点(冲突点)分开,任何穿过切口的变量均在某个冲突集上。

一个例子:

1. {A,B}

2. {B,C}

3. {¬A,¬X,Y}

4. {¬A,X,Z}

5. {¬A,¬Y,Z}

6. {¬A,X,¬Z}

7. {¬A,¬Y,¬Z}

在做出四个决策后(A, B, C, X=true),得到冲突,根据蕴含图得到A, X不能同时为真,将这个新条件加入CNF中。之后回退到A=true层。通过单位消元我们发现A=true依然是不行的(1',4,6),故将A=false加入CNF中。

发邮件问一下3.6.4.2的example。

附录

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

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

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

可靠性是指,如果有ΣA,那么有ΣA(被证明的结论就是有效的结论)。

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

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

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

设有CNF,其中C包含x1...xm

设有ILP,其中x1...xm,0xi1

对于C而言,x1¬x2x3等价于x1+(¬x2)+x31,而¬x2等价于ILP里的1x2

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

 

posted @   redwind  阅读(225)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示