OpenRules入門

オープンソースのBRMSとして、OpenRulesを試してみました。Drools もBRMSとして完成度が高く、さまざまな高度な技もできたり柔軟な対応ができますが、一方でDroolsでルールを実際に動かすまでにはそれなりに準備が必要で、(プロジェクトをきっちり行うという場合にはあまり問題になりませんが、)ちょっと気軽にルールを作って動かすというのには、個人的には若干敷居を高く感じてしまいます。

いわゆるReteアルゴリズムベースのルールエンジンの動きを学ぶだけであれば、むしろ、ちょっと古いですが、とりあえずダウンロードして動かすだけで、すぐにルールを書き始められるClipsや、もっと遡ってOPS5といったようなルールベースの他にあまり余分な要素のない言語の方が(私見ではありますが)学びやすいような気もします。

OpenRulesは、エンジンの動きこそReteベースではなく、デシジョンテーブルを上から順になめて条件の一致したルールを実行するという単純な動きですが、Excelでテーブルを作るだけで大部分が完結してしまうという敷居の低さ(特にユーザが直接ルールを書く場合など)は、ちょっと興味のあるところです。

というわけで本家のサイトの方でOpenRules 入門という記事を書き始めました。もし興味がおありであればどうぞ。

OpenRules入門

Drools 5.5.0 ファイナル リリース…とか。

久しぶりの更新になってしまいましたが、いつのまにかDrools 5.5.0 のファイナル版がリリースされていました。

ほんとに最近はDroolsの更新頻度が高くてほとんど追いつけない状態ですね。またDroolsに限らず、ここ数年、BRMSに関する話題が日本・海外を問わず増えてきており、数年前、ネットを回って事例記事などを探しても数少なかった非常に牧歌的な時代と比べれば隔世の感があります。

で、今ちょっと確認のためにGoogle トレンド で BRMS をチェックしてみました。

するとここ7~8年ほど思いのほかコンスタントに BRMS というキーワードでの検索が行われています。私の感覚としてはここ数年で増えている感じがしたのですが…。もしかすると私の感じているこの隆盛感は、具体的なBRMSの事例の露出度が高まったり、オープンソースでのBRMSがメジャーデビューしてきたことが原因なのかもしれません。

もっとも BRMS のコアである「ルールエンジン」であればオープンソース版は昔からあって、CLIPS などはその代表格。Drools も最初は、この CLIPS を参考にして作っていたはず。私もその昔、時間割作成システムのプロトタイプをCLIPSとVisual Basic 3.0(!)を使って、Windows3.1(!)上に作ったことを覚えています。結構ClipsとVB3とのつなぎが面倒だったり…今でもつなぎは面倒ですが昔に比べれば…。

そういえば先日ネットを見ていたらスウェーデンのルンド大学の大学(院)で「ビジネスルールシステムの設計」という講義科目があるのを知りました。「ルール」と言えば、昔は人工知能のプロダクションシステムなどの文脈で講義されることが普通でしたが、最近ではビジネスルールそのものをテーマにした講義もあらわれてくるようになったということですね。いや、まったく時代を感じます。

Reteアルゴリズム(3)

(前回の続き)

ちなみに前回のケースは、条件部分に書かれている

契約( $id_no : 証券番号, 基本保険金額 > 30000000)

は、制約条件が「基本保険金額 > 30000000」の一つですが、これにたとえば「○○特約付加==true」などという条件が加わると、ネットワーク上は、「基本保険金額 > 30000000」のαノードの次に、「○○特約付加==true」のαノードが直列に付け加わります。

さて次にβノードを用いた実例を見てみましょう。αノードは一つのファクトに対する制約条件でしたが、βノードは複数のファクトにまたがる制約条件を表すノードとなります。このノードは2つの入力(Left Input, Right Input)があり、左の入力はファクトの組、右の入力は、ファクトとなります。またこのそれぞれの入力は、部分的にマッチしたファクトの組やファクトの待機場所となるメモリを持ちます。

ここでは典型的なβノードであるJoinノードを例にとって見てみましょう。

先ほどのルールに加えて、もう一つルールを加えてみます。

package com.sample

declare 契約
証券番号 : String;
基本保険金額 : int;
end

declare 顧客
顧客番号 : String;
保険契約 : java.util.ArrayList;
end

rule “基本保険金額1”
when
契約(
id_no : 証券番号,
基本保険金額 > 30000000
)
then
System.out.println(“証券番号 ” + id_no + ” の基本保険金額が3000万円を超えています。”);
end

rule “基本保険金額2”
when
$c : 契約(
id_no : 証券番号,
基本保険金額 > 30000000
)
顧客(
customer_no : 顧客番号,
保険契約 contains $c
)
then
System.out.println(“顧客番号 ” + customer_no + ” の契約の中に基本保険金額が3000万円を超えている契約があります。”);
end

新たに加えた、基本保険金額2のルールは、顧客タイプのファクトに対する条件を加えています。顧客が持っている保険契約(リスト)に基本保険金額が3000万を超えるものがあればメッセージを出力するというものです。

このルールファイルは以下の形のReteネットワークとなります。

Reteネットワーク2
まず、同じ条件の部分は、2つのルールでグラフのノードが共有されていることに注意ください。さらに新たな種類のノード(黄緑のノード)が表れています。これがβノードの一種であるJoin ノードになります。

  • Joinノード
    LeftInputAdapterノードから入ってくるファクトの組(Left Input)と、Right Input
    からのファクトとを合わせて判断し、条件が満たされれば入力から新たなファクトの
    組をつくり、次に渡す。

では、上記のネットワークにファクトをinsertしてみましょう。以下のファクトを次々に加えていきます。

f-1.契約(証券番号=’10001′, 基本保険金額=40000000)
f-2.契約(証券番号=’10005′, 基本保険金額=20000000)
f-3.顧客(顧客番号=’10005′, 保険契約={f-1,f-2})

まずf-1をinsertします。契約→αノード(基本保険金額>3000万)を通過してLeftInputAdapterノードに入ります。ここでファクトの組(f-1)が生成され、2つの方向に伝播していきます。一方はTerminalノードに行き、ルール”基本保険金額1″と合わさって、実行候補のリストに付け加えられます。もう一方は、黄緑のJoinノードの左入力となります(上の図ではJoinノードの右になっていますが^^;)。

さて、Joinノードの左(上図では右)入力に(f-1)が入ってきました。Joinノードは片方の入力に値が入ってきたので、もう片方の入力のメモリを見に行きます。しかしまだメモリは空なので(f-1)はそのまま左入力のメモリに保存されます。

次にf-2をinsertしましょう。契約→αノード(基本保険金額>3000万)と行きますが、この時点でこのαノードを通過できません。

最後にf-3をinsertしましょう。顧客のファクトなので、顧客に進み、次のJoinノードの入力になります。Joinノードでは、片方の入力としてf-3が入ってきたので、f-3を右入力のメモリに保持すると同時に、左入力のメモリを見に行きます。左入力のメモリには(f-1)が入っていたので、f-3と、(f-1)を使って条件判断をします。f-3には、f-1の保険契約が含まれているので、条件が満たされ新たなファクトの組((f-1),f-3)を生成して次に渡します。最後にこのファクトの組はルール”基本保険金額2″と合わさって、実行候補のリストに加えられます。

以上、このようにしてファクトがinsertされていきます。ファクトが削除(retract)されるときは対象のファクトを入口から流して逆に削除していき、ファクトの更新の場合はretract&insertの動きになります。

以上がReteアルゴリズムの概略です。わかりにくい部分も多々あったかと思いますが、雰囲気はおおよそつかめていただけたのではないかと。

ルールベースのアプリケーションのパフォーマンス見積もりなどでは、簡単なのでついついルール数で概算見積もりをしてしまいます。しかし本来は、パフォーマンスとルール数自身とは直接の関係はありません。パフォーマンスに影響するのは、むしろルールの条件部分のバリエーションの数であるということがおわかりいただけたのではないかと思います。

あと、最近のルールエンジンの使い方から考えると、Reteアルゴリズムは少々オーバースペックなので、使い方に制限があるかわりに、より軽い Sequential アルゴリズムもよく使われますが、それはまたの機会に。

(参考文献)
Michal Bali,  Drools JBoss Rules 5.0 Developer’s Guide PACKT publishing.
Jérôme Boyer, Hafedh Mili, Agile Business Rule Development Process, Architecure, and JRules Examples Springer.

Reteアルゴリズム(2)

(前回の続き)

さて、Reteアルゴリズムは最初にルールをコンパイルしてネットワークの形に表現します。

(なお、ここではDroolsで用いられているReteOOというオリジナルReteの派生形を説明に用いますが、本質的な部分での違いはありません)。

たとえば本家サイトの方で例示しているルール(DroolsTest3.drl)をReteのネットワークにコンパイルすると、

Reteネットワーク0

となり、入口(○)が一つ、出口(●)がルール数だけある有向グラフができます。

(ちなみにDroolsのEclipseプラグインでルールファイル(.drlファイル)を表示するとReteTreeタグで上のようなReteネットワークを見ることができます)

Reteアルゴリズムは、ファクト(データ)を追加/変更/削除するたびにReteネットワークの入口から投入/チェックしていくのですが、まずはこのグラフについて最初に説明しましょう。ただ上記のルールそのままですと説明が複雑になるかと思いますので、最初にルール一つのケースから説明をします。

さて、以下のルールがあったとします。

rule “基本保険金額1”
when
契約( $id_no : 証券番号,
基本保険金額 > 30000000
)
then
System.out.println(“証券番号 ” + $id_no + ” の基本保険金額が3000万円を超えています。”);
end

このルールは以下の形のReteネットワークとなり、

Reteネットワーク1
それぞれのノードは

  • Reteノード
    ファクトを追加する際の入り口となるノード
  • EntryPointノード
    CEP(Drools Fusion)で複数のエントリーポイントを持つことができるが、今回はデフォルトのエントリーポイントのみを使用。
  • ObjectTypeノード
    ファクトのオブジェクトタイプを識別して、フィルタとして働くノード。上記のルールでは「契約」タイプのオブジェクトを通す。
  • αノード
    条件部での制約をチェックしてフィルタとして働くノード。条件部のひとつのオブジェクトに対する制約条件ひとつがひとつのノードになる。上記の例では、「基本保険金額>30000000」がこれにあたる。
  • LeftInputAdapterノード
    βノード(後述)へのエントリーポイントとなるノード。ここまで関門をくぐり抜けてきたファクトからファクトの組(tuple)を生成する。
  • Terminalノード
    if-thenルールのthen部分にあたるノード。ここまで到達したファクト(の組)がthen部分と合わさって実行候補のリストに登録される(前回の記事の1.で言う「マッチしたルールとデータのカップルを列挙」したリストに登録されるということ)

と呼ばれます。

以上がReteのネットワークの説明ですが、単にルールをネットワーク化しただけではルールの実行サイクルを効率化することには寄与しません。そこで着目するのが、前回書いたように

「ワーキングメモリの中身が変わらなければ、マッチするファクトとルールの組も変わらない。」
「1回のルール実行では、ワーキングメモリの中身はほとんど変わらない。」

という点です。ワーキングメモリにファクトが追加されたり、ワーキングメモリのファクトを変更/削除したりした都度、今作成したReteネットワークの入り口から投入し、ルールの条件にマッチするかしないかを判定します。ここで重要なことは、ファクトとルール条件との判定はルールの実行サイクルの判定のフェーズで行われるのではなく、すでにファクトをワーキングメモリに挿入したり、変更/削除したりするときに同時に判定されるということなのです。

では、上で作成したReteネットワークにファクトを挿入してみましょう。以下の3つのファクトがワーキングメモリにinsertされるとします。

f-1.契約(証券番号=’10001′, 基本保険金額=40000000)
f-2.契約(証券番号=’10005′, 基本保険金額=20000000)
f-3.顧客(顧客番号=’10005′)

この例では、ファクトf-1のみが、ルール基本保険金額1にマッチしますから、基本保険金額1(f-1)といったルールとファクトの組が、ルール実行候補のリストに追加されるはずです。

ではまず、ファクトf-1がinsertされたとしましょう。f-1は、Reteノード、EntryPointノードを通過し、Objectノードに入ります。このObjectノードはObjectの種類が契約であるというフィルタとなっていますがf-1は条件を満たすので、そのまま通過します。次のαノードは、基本保険金額 > 30000000という判定条件ですが、f-1はこれもまた満たすので通過します。次のLeftInputAdapterノードは、次に向かう準備としてf-1をファクトの組(tuple)に変換して、最後のTerminalノードに渡します。TerminalノードではLeftInputAdapterノードでできたファクトの組とルールとを組にして(基本保険金額1(f-1))、ルール実行候補リストに追加します。

次にf-2をinsertしてみましょう。f-2は、f-1と同様に、Reteノード、EntryPointノード、Objectノードは通過しますが、αノードは基本保険金額 > 30000000という判定条件を満たさないのでこれ以上は進めません。

最後にf-3をinsertしてみると、Reteノード、EntryPointノードは通過しますが、Objectノードは条件を満たさないのでこれ以上は進みません。

以上の状況を図にすると、
Reteネットワーク1-1
となります。

(つづく)

Reteアルゴリズム(1)

今日はルールエンジンの代表的なアルゴリズムReteアルゴリズムを。
ルールエンジンのアルゴリズム
にも書いたように、ルールエンジンは、

1.ワーキングメモリ(短期記憶)に入ったデータ(ファクト)の集合に対して、たくさんあるルールの条件部分をひとつひとつファクトとマッチングさせて、マッチしたルールとデータのカップルを列挙しておく。
2.カップルの中からひとつを選んで実行する。
3.実行によってワーキングメモリ(短期記憶)のファクトが書き換えられる場合もあるので、再度1.のカップリングを行う。
4.1~3を繰り返してマッチするルールがなくなったところで実行が終了する。

といったサイクルをもって実行が進んでいきます。
さてこのサイクルの 1.に注目してみましょう。ここでは論理上常にルールとファクトの組を全部マッチングさせるはずです。これを真っ正直に行っていたら誰が考えても驚くほどの時間がかかってしまうのは明らかでしょう。
というわけで、このあと一生をReteアルゴリズムに捧げる(?)こととなる Charles Forgyは考えました。「確かに論理上は、ルールとファクトの組を全部マッチングさせようとするはずだが、ルールはサイクルあたりひとつしか実行されないのでワーキングメモリの内容の変更分もたかだか一つのルールで変更された分しか変わらないはず」
・・・
「ということは、最初にまとめてルールとファクトのマッチングをチェック・結果を保持しておき後は各サイクルでのワーキングメモリの差分のみをチェック・更新していけば十分なのではないだろうか」

そして彼は、このアイデアを1979年にReteアルゴリズムとしてまとめました。
(続く)

Oracle Business Rules

は、Jessがベースとなっているそうです。Oracle Business Rules の Whitepaper には、「Oracle Rules エンジンは、Sandia Lab のJess Rules エンジンから派生した堅牢な製品で、多数のユーザーが使用しています。・・・」とはっきり・・・。今まで Oracle Business Rules は、個人的にはほとんどノーマーク(失礼!)でしたが、ちょっと興味が出てきました。今度ダウンロードしてきてさわってみようか。



JESSルールエンジン

JESSと言えばJavaによるルールエンジンの草分けであり、もっとも有名なうちの一つでしょう。今でもバリバリの現役・・・???
と思って先日ネットを調べていたらこんなニュースリリースがありました。

JESSがUSA海軍の次期ミサイル駆逐艦に採用される。

どうも、次期ミサイル駆逐艦の制御や警報装置などに組み込まれるようです。

JESSは、CLIPS(CLIPS超入門も参照)のシンタックスを受け継いだLispライクな言語を持つルールベースエンジン。Javaが生まれて間もないころすでに最初の版が存在していた歴史ある言語です。個人的には、Lispライクなシンタックス(→実はJava風のシンタックスよりも好き)を持っており、Javaともうまくインターフェースがとれているので結構好きなのですが、ライセンスがCLIPSやDroolsと比べるとオープンでなくなかなかちょっと試す以上のことまではできていません。

ところで、JESSは歴史のあるルールエンジンだけあって、JESS IN ACTIONというちゃんとした成書があります。この本にはJESSの入門から比較的まとまったJESSアプリケーションプログラムのことまでが記載されており、(JESSによる)ルールベースを本できちんと学びたいという方にはこの本はなかなかよいのではないかとおもいます。(その点Droolsは、若くて発展途上なのできちんとした本がありません。実はアマゾンで1冊だけ”Pro Drools・・・”という洋書を見つけたのですが、発売後すぐ品切れになってしまいました。)

JESS IN ACTIONは今、ちょっとamazonで調べたところ品切れになっているようですね。