1. 程式人生 > >ASP(Answer Set Programming)程式設計入門

ASP(Answer Set Programming)程式設計入門

ASP 是什麼?

ASP 的全稱是 Answer Set Programming,中文翻譯為問答集程式設計。對於它的解釋,有這麼幾個要點。

第一,它屬於宣告式程式設計的一種。宣告式程式設計,簡而言之就是告訴計算機你想“做什麼”,而不是“怎麼做”。SQL 是很多人都熟悉的宣告式程式設計的例子。如果要從一個數字集合裡找出所有小於10 的數字,怎麼做?

select * from tab where val < 10

但如果是指令式程式設計,怎麼做?

List result;
for i in collection {
    if (i < 10) {
        result.add(i);
    }
}

不要糾結語法,重點關注一下表達方式的區別。

第二,它主要是針對複雜的搜尋問題。搜尋問題可能是從一些儲存在資料結構中的資料裡獲取一些資訊,也可能是計算滿足某一問題約束所有可能的結果。前者比如說上面提到資料檢索問題,後者比如說最短路徑問題。

第三,它是基於穩定模型語義的邏輯程式設計。邏輯程式設計通常是指用一組邏輯語言(符號)來描述關於某個問題的事實和規則。而穩定模型語義是一個挺複雜的東西,可以看看這篇文章的介紹。這裡面涉及到一個概念叫做失敗即否定(negation as failure )。我認為,它的意思就是,基於當前的事實和規則推斷失敗的,即為否定。當然,由於事實可能並不全面,在增加事實之後,結果可能不是否定的。這就又涉及到 “非單調推理” 的問題了,前面提到的部落格裡面有所涉及。

一些參考概念
程式設計正規化
ASP
搜尋問題
穩定模型語義

如何執行 ASP 程式

波茨坦大學開發了一個程式集,叫做 Potassco(Potsdam Answer Set Solving Collection)。其中的 clingo 可以用來執行 ASP 程式。

clingo 中有兩個子程式:

  • gringo 用來翻譯使用者寫的邏輯程式
  • clasp 用來求解

在官方網站上,有非常詳細的文件介紹 Potassco,有興趣的讀者可以去參考。一般來講,執行一個 ASP 程式就像下面這樣一條命令:

$ clingo files/crime.lp

其中的 .lp 檔案就包含了 ASP 程式碼。

這裡可以下載 clingo,在本地解壓之後,目錄下就有可執行程式了。

如何編寫 ASP 程式

在 Potassco 的文件裡,有詳細的語法說明。如果有時間,建議好好看看。我這裡就簡單的說一下思路。

一個簡單的 ASP 程式可能包含三個部分:

  • 事實
  • 規則
  • 輸出

其中事實和規則部分,用來描述問題的。輸出部分,用來檢視結果。所以,前者是最重要的。整個 ASP 程式的核心目的就是:把問題描述清楚了,自然就能得到正確的結果。而 gringo 也提供了不少的語法符號,來幫助我們做到這一點。後面的章節我會拿出一個小例子來作說明。這篇部落格 也介紹了很多基礎知識。

一個例項

圖1

% fact
p(1..4).

e(1, (3;4)).
e(2, 4).
e(3, (1;4)).
e(4, (1;2;3)).

% rule
c(X, Y) :- p(X), p(Y), not e(X, Y), X != Y.

% Display
#show c/2.

上面是一個非常簡單的例子,僅供參考。我們來求圖中沒有邊直接相連的點。

前 5 行程式碼是事實部分,它如實描述了 圖1 的情況。p 表示點,e 表示邊。p(1…4) 這種寫法等同 p(1). p(2). p(3). p(4). 。其中 “.” 是一句程式碼的結束符。e(1, (3;4)). 等同於 e(1, 3). e(1, 4),表示 1 到 3、4 有邊。1…4 和 “;” 都是語法糖。

所以,事實部分就是在說,有 4 個點,並且他們之間有的有邊,有的沒有。

第 6 行程式碼是規則部分。“:-” 我們可以理解為:“如果它後面的條件成立,那麼前面就成立”。而 “,” 則表示“並且”的意思。“not” 顧名思義,表示否定。所以 c(X, Y) 成立的條件就是:“有兩個點 X 和 Y,他們之間沒有邊,並且 X,Y 不是同一個點。”

這樣,我們發現,c(X, Y) 實際上就是要求的結果。所以,在最後一行輸出了 c。這裡 #show 表示輸出,c/2 中的 c 表示 c(X, Y),而 2 則表示 c 有兩個元素。

這個程式的執行命令如下:

./clingo test -n 10

後面的 “-n 10” 表示輸出多少個解。有時候,程式可能有不止一個解。但我們的程式只有一個解,下面是輸出結果:

clingo version 5.3.0
Reading from test
Solving...
Answer: 1
c(2,1) c(1,2) c(3,2) c(2,3)
SATISFIABLE

Models         : 1
Calls          : 1
Time           : 0.001s (Solving: 0.00s 1st Model: 0.00s Unsat: 0.00s)
CPU Time       : 0.001s

上面的 c(2,1) c(1,2) c(3,2) c(2,3) 就是結果了。大家也可以思考一下,怎麼能夠讓 c(2,1) 和 c(1,2) 只保留一個呢?

參考資料

資料1
資料2
資料3
資料4