這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Rete演算法是Charles Forgy在1979年的論文中首次提出的,針對基於規則知識表現的模式比對演算法。目前來說,大部分規則引擎還是基於rete演算法作為核心,但都有所改進,比如drool,jess等等,下面介紹rete演算法的概念
1.rete 演算法
Rete演算法是一種高效的模式比對演算法用來實現產生式規則系統
(空間換時間,用記憶體換取匹配速度)
它是高效的演算法,它通過緩衝避免了相同條件多次評估的情況,但是帶來了大量的記憶體使用量
Rete 在拉丁語中是 ”net” ,有網路的意思;Rete演算法通過規則條件產生了一個網路,每個規則條件是網路中的一個節點
rete可以被分為兩部分:規則編譯和運行時執行。規則編譯是指根據規則集產生推理網路的過程,運行時執行指將資料送入推理網路進行篩選的過程。
2.規則編譯
規則編譯就是根據規則檔案推理產生網路的過程。下面展示一下推理後的網路節點圖。(大家只用看一下,心裡有個印象就好,下面我會分析講解)
圖一
2.1 Root Node:
(一的黑色節點) 根節點(RootNode)是所有對象進入網路的入口,然後進入到TypeNode
2.2 Type Node
(有的叫ObjectTypeNode,圖一的紅色節點): type Node 就是我們的fact 也就是我們規則所用到的pojo;每個fact 就是一個 type node。 type Node 就是類型檢查,引擎只讓匹配Object 類型的對象到達節點,它能夠傳播到 AlphaNodes、LeftInputAdapterNodes (作為betaNodes的左端輸入節點)和 BetaNodes
舉例:有兩個fact ,Person 和cheese 。那麼節點圖就如下所示。
2.3 Alpha Node
:(圖一的藍色節點)用來評估字麵條件,例如。person.age>10 這就是一個 alpha node 節點,當一條規則有多條字麵條件,這些字麵條件被連結到一起。
Drools 通過散列法最佳化了從 ObjectTypeNode 到 AlphaNode 的傳播。每次一個 AlphaNode 被加到一個 ObjectTypeNode 的時候,就以字面值( literal value )作為 key ,以 AlphaNode 作為 value 加入 HashMap 。當一個新的執行個體進入 ObjectTypeNode 的時候,不用傳遞到每一個 AlphaNode ,它可以直接從 HashMap 中獲得正確的 AlphaNode ,避免了不必要的字面檢查。
舉例 1:條件 Cheese (name=”cheddar” ,strengh==”strong”)
解釋:name 和 strengh 都是對象Cheese 的屬性,且name 和 strengh兩個條件的關係是且,必須同時滿足拿麼節點圖如下所示:
舉例 2:在舉例1 的條件上添加另外一條規則 Cheese (name=”cheddar” ,age>10)
解釋:name 和 age 都是對象Cheese 的屬性,且name 和 age兩個條件的關係是且,必須同時滿足,拿麼結合舉例1的節點圖,如下所示:
此時我們發現我門共用了(name==“cheddar“) 節點。
2.4 Bate Node:
(一的綠色節點)用來對2個對象進行對比、檢查。約定BetaNode的2個輸入稱為左邊(Join Node)和右邊。左邊通常是一個a list of objects,右邊(NotNode)通常是 a single object。每個Bate節點都有自己的終端節點等組成
BetaNode 具有記憶功能。左邊的輸入被稱為 Beta Memory,會記住所有到達過的語義。右邊的輸入成為 Alpha Memory,會記住所有到達過的對象。
舉例:條件 Cheese (name=”cheddar” ) Person(favouriteiteCheese==”cheese.name”)。
解釋:這個兩個對象Cheese和Person 的關聯操作。Cheese的name=”cheddar” 且cheese.ame ==favouriteiteCheese。那麼節點圖如下所示。
圖中黃色的node 我門稱為LeftInputAdapterNode,這個節點的作用是將一個single Object轉化為一個單對象數組(single Object Tuple),傳播到JoinNode節點。因為我們上面提到過左邊輸入通常是a list of objects。
舉例2:
rulewhen Cheese( $cheddar : name == "cheddar" ) $person : Person( favouriteCheese == $cheddar )then System.out.println( $person.getName() + " likes cheddar" );endrulewhen Cheese( $cheddar : name == "cheddar" ) $person : Person( favouriteCheese != $cheddar )then System.out.println( $person.getName() + " does not like cheddar" );end
網狀圖如下:
Drools 通過節點的共用來提高規則引擎的效能。因為很多的規則可能存在部分相同的模式,節點的共用允許我們對記憶體中的節點數量進行壓縮,以提供遍曆節點的過程
從圖上可以看到,編譯後的RETE網路中,AlphaNode是共用的,而BetaNode不是共用的。上面說的相等和不相等就體現在BetaNode的不同。然後這兩條規則有各自的Terminal Node。
2.5 建立 rete 網路
Rete 演算法的編譯結果是建立了規則集對應的 Rete 網路 , 它是一個事實可以在其中流動的圖。建立 rete 網路的過程 [1]如下:
1) 建立根節點;
2) 加入一條規則
a. 取出規則中的一個模式 ,(模式就是規則中的最小一個匹配項例如(age>10,age<20)拿麼age>10 就是一個模式,age<20 就是另一個模式。)檢查模式中的參數類型,如果是新類型(也就是新的fact類型),則加入一個類型節點;b. 檢查模式 對應的 Alpha 節點是否已存在,如果存在則記錄下節點位置,如果沒有則將模式 作為一個 Alpha 節點加入到網路中,同時根據 Alpha 節點的模式建立 Alpha 記憶體表;c. 重複 b 直到所有的模式處理完畢;d. 組合 Beta 節點,按照如下方式: Beta 左輸入節點為 Alpha(1),右輸入節點為 Alpha(2) 。Beta(i) 左輸入節點為 Beta(i-1),右輸入節點為 Alpha(i) i>2 並將兩個父節點的記憶體表內聯成為自己的記憶體表; e. 重複 d 直到所有的 Beta 節點處理完畢;f. 將動作(Then 部分)封裝成分葉節點(Action 節點)作為 Beta(n) 的輸出節點;
3) 重複 2) 直到所有規則處理完畢; 執行完上述步驟,建立的 rete 網狀圖
3. 運行時執行
WME :儲存區儲存的最小單位是工作儲存區元素(Working Memory Element,簡稱WME),WME是為事實建立的元素,是用於和非根結點代表的模式進行匹配的元素。
Token:是WME的列表,包含有多個WME,(在Forgy的論文中,把Token看成是WME的列表或者單個WME,為了闡述方便,本文將把Token只看成WME的列表)
(1)如果WME的類型和根節點的後繼結點TypeNode(alpha結點的一種)所指定的類型相同,則會將該事實儲存在該TypeNode結點對應的alpha儲存區中,該WME被傳到後繼結點繼續匹配,否則會放棄該WME的後續匹配;
TypeNode儲存: 每次一個AlphaNode被加到一個 ObjectTypeNode的時候,就以字面值(literal value)也就是file 作為key,以AlphaNode作為value加入HashMap。當一個新的執行個體進入ObjectTypeNode的時候,不用傳遞到每 一個AlphaNode,它可以直接從HashMap中獲得正確的AlphaNode,避免了不必要的字面檢查。
(2)如果WME被傳遞到alpha結點,則會檢測WME是否和該結點對應的模式相匹配,若匹配,則會將該事實儲存在該alpha結點對應的儲存區中,該WME被傳遞到後繼結點繼續匹配,否則會放棄該WME的後續匹配;
alpha 儲存:檢測WME是否和該結點對應的模式相匹配,若匹配,則會將該事實儲存在該alpha結點對應的儲存區中,該WME被傳遞到後繼結點繼續匹配
(3)如果WME被傳遞到beta結點的右端,則會加入到該beta結點的right儲存區,並和left儲存區中的Token進行匹配(匹配動作根據beta結點的類型進行,例如:join,projection,selection),匹配成功,則會將該WME加入到Token中,然後將Token傳遞到下一個結點,否則會放棄該WME的後續匹配;
bate儲存區:每個非根結點都有一個儲存區。其中1-input(alpha)結點有alpha儲存區和一個輸入口;2-input(bate)結點有left儲存區和right儲存區和左右兩個輸入口,其中left儲存區是beta儲存區,right儲存區是alpha儲存區。儲存區儲存的最小單位是工作儲存區元素(Working Memory Element,簡稱WME),WME是為事實建立的元素,是用於和非根結點代表的模式進行匹配的元素。
(4)如果Token被傳遞到beta結點的左端,則會加入到該beta結點的left儲存區,並和right儲存區中的WME進行匹配(匹配動作根據beta結點的類型進行,例如:join,projection,selection),匹配成功,則該Token會封裝匹配到的WME形成新的Token,傳遞到下一個結點,否則會放棄該Token的後續匹配;
(5)如果WME被傳遞到beta結點的左端,將WME封裝成僅有一個WME元素的WME列表做為Token,然後按照(4)所示的方法進行匹配;
(6)如果Token傳遞到終結點,則和該根結點對應的規則被啟用,建立相應的Activation,並儲存到Agenda當中,等待激發。
(7)如果WME被傳遞到終結點,將WME封裝成僅有一個WME元素的WME列表做為Token,然後按照(6)所示的方法進行匹配;
以上是RETE演算法對於不同的結點,來進行WME或者token和結點對應模式的匹配的過程。
4. Rete 演算法的特點:
a. Rete 演算法是一種啟發學習法演算法,不同規則之間往往含有相同的模式,因此在 beta-network 中可以共用 BetaMemory 和 betanode。如果某個 betanode 被 N 條規則共用,則演算法在此節點上效率會提高 N 倍。
b. Rete 演算法由於採用 AlphaMemory 和 BetaMemory 來儲存事實,當事實集合變化不大時,儲存在 alpha 和 beta 節點中的狀態不需要太多變化,避免了大量的重複計算,提高了匹配效率。
c. 從 Rete 網路可以看出,Rete 匹配速度與規則數目無關,這是因為事實只有滿足本節點才會繼續向下沿網路傳遞。
5. Rete 演算法的不足:
a. 事實的刪除與事實的添加順序相同, 除了要執行與事實添加相同的計算外, 還需要執行尋找, 開銷很高 [3]。
b. RETE 演算法使用了β儲存區儲存已計算的中間結果, 以犧牲空間換取時間, 從而加快系統的速度。然而β儲存區根據規則的條件與事實的數目而成指數級增長, 所以當規則與事實很多時, 會耗盡系統資源 [3]。
針對 Rete 演算法的特點和不足,在應用或者開發基於 Rete 演算法的規則引擎時,提出如下建議:
a. 容易變化的規則盡量置後匹配,可以減少規則的變化帶來規則庫的變化。 b. 約束性較為通用或較強的模式盡量置前匹配,可以避免不必要的匹配。 c. 針對 Rete 演算法記憶體開銷大和事實增加刪除影響效率的問題,技術上應該在 alpha 記憶體和 beata 記憶體中,只儲存指向記憶體的指標,並對指標建裡索引(可用 hash 表或者非平衡二叉樹)。 d. Rete 演算法 JoinNode 可以擴充為 AndJoinNode 和 OrJoinNode,兩種節點可以再進行組合 [5]。