ASP 回答集编程 gringo指导

ANSWER SET PROGRAMMING

Section 1 前言

这个教程主要是介绍gringo、clasp、clingo和iclingo。..

第二节,通过一个例子达到介绍使用逻辑程序建模和gringo这种建模语言。

第三节,通过介绍我们整合了gringo和clingo的工具的输入语言。并在第四节中会阐释三个著名的例子。第五第六节则会介绍一些比较特殊的方面,并给出小提示和有用的相关错误以及警告的命令行选项。

这个教程中不会做过多的理论背景介绍。1153 8008203588

如果你熟悉lparse(一种经典的smodels前端执行器,反正我是不知道),那么附录A中会列出它跟我们这个工具的主要区别。。。。。。。。balabala

后面有很多例程,大部分都是能运行的,‘\’这个符号意味着后面要接一段字符。OK,现在要开始了哦。

wps_clip_image-22077

Section 2 小例程

这节,我们会通过一个简单的hanoi puzzle来展示gringo的强大之处。这有三个木杆和一些不同大小的能放置在木杆上的盘子。目标是把最左边那个木杆的所有盘子移动到最右边,盘子只能放置比他自己大的盘子上。这里暂且不讨论最优方案,而仅仅关注如何解决。

在ASP中,习惯于一种统一的问题描述。从这个方法论出发,我们把编码从下面这个问题中分割出来:给定了初始位置的盘子,一个目标,一个数字n,问是否有一个长度是n的序列满足给定的条件。我们可以看出这个问题就很优雅的被简化为一个能够用ASP解决的描述性问题

2.1  例子

用谓词peg/1和disk/1来表示木杆和盘子。用正整数来表示盘子,越小的数字表示越大的盘子。木杆呢就有自己的名字,谓词init_on/2 和 goal_on/2 表示的是初始和目标状态。 第一个参数是盘子的数量,第二个参数是盘子被放置在初始和目标状态上的木杆。最后,谓词moves/1 表示达到目标状态的步数。注意,这个问题的条件是三个木杆和固定的初始与目标状态。通过ASP 我们可以很简单的转变这个要求和编码,下面会结合特定数量的木杆和某种初始与目标状态来介绍。Figure 1是一种可能的例子,虚线那个就是目标状态,用ASP编程的话就是:

wps_clip_image-15033

1中的“;”是一种拓展了的语法糖(section 3.1.9),代指peg(a) , peg(b) , peg(c)这三个木杆。同理,2中的也是一种语法糖(3.1.7),代指disc(1),disc(2), disc(3) , disc(4) ,这四个盘子。4、5是初始与目标状态。最后一行给出了解决这个问题的步数。

2.2 问题编码

现在可以用 non-ground 规则(3.1.1)处理hanoi的编码了。规则中的变量在各个实例中是独立的。通常一个编码会包括一个 Generate,一个Define和一个Test。我们也遵从这个范例,并用%作为行注释的开头。变量D,P,T和M分别指代disks盘子,pegs木杆,步数序列中的第T步,和这个序列的长度。

wps_clip_image-27593

Generate部分只有一行,即第2行。一步的意思即把任意的一个盘子放到任意的一个木杆中。规则的头部被称为cardinality constraint(3.1.10),包含一个用谓词扩展冒号的集合(3.1.8),并且是用下限与上界范围的。这个constraint是真的,当且仅当这个范围里的所有子集满足时。另外这个constraint被用在规则的头部,除了测试(不解)还可以引导出一个新的原子,在我们这个例子中就是指盘子的步数。注意到,我们现在并没有限制步数。当目前为止,任何盘子都可以被移动到任何木杆上,而不必考虑任何限制条件。

接下来,我们给出新的辅助谓词,它并不直接满足问题但是被用于后面的Test测试部分。第4行的规则指出一步的目标木杆,谓词move/2 用在我们移动一步时的盘子,但是并不介意放在哪。用谓词on/3来表示hanoi问题每一步的状态。前面两个参数给出盘子在第三个参数时的位置,第3个参数是执行到第几步的意思。下一行,第5行指出在初始状态(0 步时)时的每个盘子的位置。我们用规则6和7来描述状态的转移。6行的意思很明确。注意一下not moves(T)的用法,由于最后一步之后就不会再改变了所以是有限的步骤。7行的规则确保盘子的每一步都是会移动的。

wps_clip_image-21054

如果下次一次没有移动这个盘子,那么这个盘子仍然在原来的位置。(不过我不理解后面的not moves(T)是啥意思)。。。。

最后我们来定义一下辅助谓词blocked/3

wps_clip_image-12895

这是用来记录不能放盘子的木杆的位置w.r.t。第8行说明一些盘子在一些木杆上是blocked的,即下一步的时候不能把编号较小的(也就是面积较大的)盘子放上来。9行如果盘子是在blocked阻塞的位置上就意味着这个盘子blocked阻塞了。Blocked阻塞的位置被标记为0,这对断言一个多余的步骤很方便。

最后,test部分构建了Generate和Define部分不能全部满足的条件。第一个完整的限制条件是11行,倘若在某个木杆上的编号小点的盘子在第T步时是blocked的,说明已经有小盘子在那里了。如果是恰好小一个单位的盘子已经在那里了,则此时也不能重复放置;如果是小2个以上单位的盘子在那里,那这个小一个单位的盘子也放不上。看看D-1这个的用法,盘子是不能被重复放到同一位置的。12的限制条件,盘子只能被放在比他大的盘子上,如果她的下一步是blocked的,则下一步不能移动了。13行,是目标状态。为了让编码更有效率,14行添加了一个冗余条件:每个盘子在任何时刻都只能被放置在一个木杆上。尽管上面的条件已经暗含这个了,但添加这个额外的领域知识能够提高解题速度。后面的两行在解出时控制谓词的打印。15隐藏了所有谓词而只展示了move/3

不理解之处:not moves(T),13行

2.3 题解

终于到解题的时候了(先确定你是否安装了clingo、gringo、clasp,最好是能添加进系统变量的path中)。

wps_clip_image-5678

wps_clip_image-25120

除了SATISFIABLE之外,还有UNSATISFIABLE,UNKNOW.。那个1+是只目前找到了一个,但其实搜索空间中还没找完,也许还有解答。

3 输入语言

这节简要阐释gringo所用的语言,结合clingo、以及递增(翻译执行器。。。这个翻译不靠谱,我实在搞不清楚incremental grounder and solver是神马意思)iclingo,solver clasp。3.1节会介绍gringo和clingo的输入语言。3.2则会用一些指令拓展iclingo。最后在3.3节则主要是clasp的操作。

3.1 gringo和clingo的输入语言

Gringo是把逻辑语言翻译成等价的ground 语言。Gringo的输出可以通过管道输入到clasp,然后计算出回答集。Clingo的内部包含了gringo和clasp,也即两只功能都有。

通常文本文档中明确的逻辑语言被gringo或者clingo通过命令行调用。下面我们将描述gringo和clingo的输入语言。

3.1.1常规语言与完整性约束

wps_clip_image-19546

每种逻辑语言都是用术语构成的(应该得先做词法分析吧)。Figure 2 简易阐释了gringo的术语。最基本的术语就是整数、常量和变量。除此之外,还有一些特殊变量和常量。匿名变量用’_’(下划线)表示,它近似于普通的变量只不过任何一次‘_’的出现都被认为是另一个变量(那种没有重复利用价值的变量就可以用这个啦)。额外的,还有两种特殊常量’ #supremum’ 和’ #infimum’,上确界和下确界。最后,用其他术语构成函数标示。更复杂的术语将在以后介绍。

规则的构造:

wps_clip_image-18150

规则或者事实的头部A0是一个原子,类似函数标示或者常量。关于这些语法的解释,可以参照一片博客http://blog.tuidao.me/2012/04/answer-set-programming/。。

引用“

一个ASP程序包括两样重要的部分:事实,用于描述现实世界的状态。还有规则,用于进行推理,并且他们都由英文句号'.'结尾。而规则又分为在符号':-'左边的结论和右边的条件。结论成立,如果右边的条件被满足。另外事实也可以看做是省略了符号':-'和右边所有条件的规则。更细分一点,每一个ASP程序里还包括了一些常量(在这里就是城市名),谓词(这里的road,arrive什么的),每个谓词有若干个参数,并且为了简化起见,我们把带有n个参数的谓词p写作p/n。最后当然还有变量(在这里就是大写字母X和Y),需要注意的是本质上ASP程序是不支持变量的,这里的变量仅仅是为了方便书写,在实际的求解中这些变量会被替换为程序中出现的所有常量,这也是我们为什么需要先运行gringo程序的原因,它的作用就是将程序里所有变量挨个替换成程序里出现的所有常量,你可以用:

 

  gringo --text road

来看看程序被替换后的样子。

现在让我们来定义一下ASP程序里各种规则的样子,有了这些,我们就可以用它来解决问题了。

1. 约束规则

所有在符号':-'左边什么都没有的规则就是约束规则,它表示右边的条件不能同时被全部满足。

2. 选择规则

所有在符号':-'左边形如 {a1,a2,...,am} 的规则就是选择规则,其含义是如果右边条件成立,那么左边的任意一个子集也成立。

下面我们来考虑一下对于选择规则的扩展,首先我们不限制{a1,a2,...,am}这样的元素只能出现在规则左边,而是也可以出现在规则右边。其次,任意子集的说法也未免太简单了,我们给它加上个数限制,像l{a1,a2,...,am}u,这个元素表明花括号中的m个元素最少有l个最多有u个成立。所以形如

a :- 2 {b, c, d, e} 3.

这样的式子就表明如果b, c, d, e中最少有两个最多有三个元素成立,那么a也成立。当然,这里的l或者u并非必须同时出现,只写一个也是可以的,含义也很好理解。

3. 条件元素

数学里大家想必遇见过诸如 {a|a∈A} 这样的东西,它表示了所有满足 a 属于 A 的元素 a 的集合,这里我们也有类似的东西。比如说顶点着色问题,假设我们有诸如 vertex_color(X, Y) 这样的元素,我们想让它代表顶点X被染了颜色Y,并以此得到某一顶点v1所有的染色可能性。但是我们之前也说了,gringo会将程序里的所有变量拿所有常量替换一遍,这样Y不仅会被拿颜色替换,同时也会被拿顶点替换,这显然不是我们想要的,这时我们就需要条件元素了:

vertex_color(v1, Y):color(Y) :- vertex(v1).

看到了么?只需要用冒号':'就可以将变量Y限定在颜色的范围内,这样我们得到的就是顶点v1所有的着色可能了。需要说明的是条件并非只能加一个,如果你有多个变量需要做限制的话,可以将冒号一直连下去。

那么冗长无聊的语法介绍就先到此为止吧,我们来用一个例子来介绍如何来撰写一个ASP程序。我们所用的例子就是著名的八皇后问题,如何在8*8的国际象棋棋盘上摆放8个皇后而使他们无法相互攻击。

首先是事实,我们的棋盘有8行8列:

1         row(1..8).

2         col(1..8).

这里我们用1..8来代表从1到8的简写,这样的式子会被展开为row(1). row(2).等等直到row(8).

之后就是各个规则了,首先我们需要在 I 行 J 列上放置皇后:

queen(I, J) : row(I) : col(J).

这还不够,因为我们放且只能放8个皇后,所以我们把这个规则改写一下:

8 {queen(I, J) : row(I) : col(J) } 8

好了,到此为止如果你好奇的话可以像上面的例子那样先执行一下看看结果是什么,不过千万要记得把clasp后面的0换成5来表示只计算5种满足条件的结果,毕竟我们现在可以说什么限制都没有加,所有结果的数量多的可怕。另外,因为我们只关心queen的情况,而你会发现程序把row和col的结果都打印出来了,所以我们可以用

 

#hide.

2 #show queen/2.

来将结果显示限定在谓词queen上而隐藏其他的谓词。好了,到现在你会发现得到的结果只是随便把8个皇后放到棋盘上,根本没有考虑他们之间有没有攻击的问题,那么之后我们就来添加这些限制,首先是每行每列上不能同时有两个皇后:

 

:- queen(I, J1), queen(I, J2), J1 != J2.

2 :- queen(I1, J), queen(I2, J), I1 != I2.

最后,对角线上也不能同时有两个皇后:

 

:- queen(I1, J1), queen(I2, J2), I1 != I2,

J1 != J2, I1 - J1 == I2 - J2.

:- queen(I1, J1), queen(I2, J2), I1 != I2,

J1 != J2, I1 + J1 == I2 + J2.

到现在为止我们的程序就完成了,运行来看看结果吧。很有趣不是么,我们没有教计算机如何计算,只是描述了整个问题,计算机就把结果告诉了我们。当然这里介绍的只是冰山一角,限于篇幅和我的水平也没法更加详细的介绍了,如果你有兴趣的话,这里有不少文档可以供参考,如果能把你拉下水,那么我的目的也达到了。

注:也许很多人在这里的第一反应是用否定的 ¬block(X,Y). 而不是什么奇怪的not,这其实牵扯到非单调推理的问题,不过限于篇幅这个就不扯了,大家可以自己去找找资料。

Gringo期望规则是安全的,即所有的出现在规则中的变量会被一些正的短语所替换。一个谓词中包含变量,则称这个谓词绑定了这个变量。另外,这些推到必须是非循环的。一个简单的例子, a :- b .   B :- a . 这个程序的回答集是空集。Finally, note that default negation is ignored when checking for acyclic derivations (we do not need a reason for an atom being false)。默认的否定被用来表示选择。例如,a :- not b .   b :- not a,有两个回答集{a} {b}。只不过一般我们不用这个来表示选择,像是第2节中的例子,我们用cardinality constraint,因为它更具有可读性。

3.1.2 经典否定

逻辑程序中,not表达的是默认否定,即短语 not A是指不需要A来推导。相反,在一些命题的经典的否定中,指命题的补集能够推导出。经典否定中是用原子前的标示’-’符号。如果A是原子,那么-A就是A的补集。从语义上理解,-A只是一个简单的新原子,A与-A不能同时满足。经典否定只是一个语法特性,能通过完整性约束实现。

Example 3.1

wps_clip_image-325

下面用完整性约束来实现经典否定

wps_clip_image-4881

这个程序有两个回答集,{ files(tweety)}   {-flies(tweety)}。为什么????

我执行过后回答集是{files(tweety)}   {-files(tux)}

现在我们添加一个新的事

实:

8    files ( tux ) .

此时,通过经典否定这个新程序就不再有回答集了。可能的回答集同时包含files(tux) 和 -flies(tux),这便和完整性约束的第6行冲突了。

3.1.3 disjunction析取

规则头部的原子之间可以用 ‘ | ’连接。额外的,逻辑程序必须满足的最低限度标准(这篇教程不会细讲)。一个简单的程序 a | b 。有2个回答集 {a} 和{b},但是却不允许a,b因为这不是最小的模型。

通常,disjunction析取会增加计算的复杂性。这也就是为什么clingo、assat、clasp、nomore++、smodels、等不想解决disjunctive programs。claspD,cmodels或者gnt需要被用来解决disjunct program。我们建议使用“choice construct”选择结构(3.1.10)而不是disjunction,除非这非常有必要。

3.1.4 内建算数函数

Gringo和clingo支持不少算术函数。下面的标示就是这些函数:+ 加,- 减,* 乘,/ 或者 #div 整数除,\ 或者#mod 求余,** 或者#pow 次方,|·| 或者 #abs 绝对值, & 与,? 或,^ 或非, ‐ 补。

Example3.2

wps_clip_image-7718

wps_clip_image-29160

变量L和R是7 和2的实例,‘(’前的那些空格是可以无视的,而#div和#mod后面的空格则是强制性的,还有那些需要用括号包起来的。

提醒一点,算术函数作用域内的变量并不受相关原子的约束。例如,规则 P(X) :- P(X+1) 这是不安全,但是 P(X-1) :- P(X)是安全的。尽管后面这种会产生无尽量的式子,但是gringo自己会避开这种输入

3.1.5 内建比较谓词

下面的内建谓词在规则体内包含比较词: == 相等, != 不等 , < 小于 <=。。。。。。。

Example 3.3 用法

wps_clip_image-13396

最后2行指出,事实上,算术函数在比较谓词之前被估算,然后在比较那2个整数。所有的比较谓词能够用在任意的词法中。

wps_clip_image-7659

整数是用普通的比较方式,而常量采用字典序。函数标示首先用他们的参数数量来比较。如果参数数量不同,函数标示的名称用用字典序比较。如果名称也不同,then the arguments are compared component wise(不理解)。最后,整数总是被常量小,而常量则比函数标示小。

需要明确,内建比较谓词不能绑定变量。当检测哪个规则是安全的时,比较谓词不会被考虑。

3.1.6 赋值

内建谓词  := 和 = 被用在规则体中,类似赋值吧

Example 3.4 这个例子讲述如何将变量赋值给术语。

wps_clip_image-20678

第3行包含4个赋值,右侧的那些直接或者间接依赖于X和Y。这2个变量在第5行中用谓词的原子 num/1 绑定。同时也看到了内建比较谓词==的另一种用法。

num(X),num(Y),限定了X和Y是1-5.同时又X < Y,且 Y1=Y+1.。。。这根本就是解方程,只不过这是用描述方程的方式。

Example 3.5 这次我们换成在左边来用  :=

wps_clip_image-8901

注意一下 f (a,X,X+1)。X+1会被推迟,稍后再检查。所以,第4行等价于:

wps_clip_image-195

赋值语句也是可以绑定变量的,但是循环赋值就不行了。有一个限制是,右边的赋值语句需要绑定到一些谓词中(不理解)。

3.1.7 区间

在例子3.4中,有5个事实, num(k)表示连续的整数k。Gringo和clingo支持i..j的整数区间,也即 i <= k <= j,会在类似编译的时候扩展处理。

Example 3.6

wps_clip_image-28612

很简单。。。

举个例子,X是9则第4行:

wps_clip_image-14595

拓展出来

wps_clip_image-18165

3.1.8 条件

这点我在上面的引用的博客中也指出了。 :

Example 3.7 在规则体和规则的头部应用了 条件语句

wps_clip_image-6849

5、6相当于

wps_clip_image-13922

这个容易理解。。。。。。

也可以用复合条件,比如加上 not

条件语句有三个要点:

1,右侧的原子谓词需是domain 谓词

2,条件语句中出现的变量被视为local的,也即在条件之外不能用绑定变量。条件语句之外的变量是全局变量,条件语句前端原子钟的变量必须是在右侧的或者全局的。

3,全局变量的优先级比较高。

3.1.9 pooling汇总

符号“;”,这使得程序更加简洁。例如一开始的例子中

peg ( a ; b ; c ).   相当于 peg( a). peg( b). peg( c).

位于规则体中的“;”是用连接符扩展,而规则头的则是扩展成多个规则

Example 3.8

wps_clip_image-11934

把规则3扩展一下可得

wps_clip_image-29700

另外,还有符号“;;”,用于分割谓词的参数。它并不在一个术语中使用而是用来列出谓词的参数,具体的扩展方式与“;”一样。

Example 3.9 “;”与“;;”的区别

wps_clip_image-11063

2与3相应的扩展成:

wps_clip_image-31324

3.1.10 聚合

在一个词法集合中估算正确性。其形式为:

wps_clip_image-19939

有上界u,下界l。操作符 op,集合 Li,,每个元素有权重 Wi。如果用这个操作符操作每个元素其权重介于区间之内就为真。Gringo支持聚合 #sum (所有权重之和), #min (最小权重),#max (最大权重) #avg (平均权重)。还有3个句法不同的聚合,

wps_clip_image-29402

#count 等价于 所有权重为1时的#sum

还有wps_clip_image-15866

如果Li为真的数量是偶数则式子为真。       反之

通常权重都默认为1 。操作符也被默认为 #sum。

posted @ 2013-03-12 10:24  gausszh  Views(2794)  Comments(0)    收藏  举报