これは完全な答えではありませんが、ウィキペディアの記事からネットワーク例の可能なエンコードを次に示します。各ノードには、名前、後続ノード (子ノード) のリスト、および確率テーブルがあります。
(defn node [name children fn]
{:name name :children children :table fn})
また、真/偽の確率を構築するための小さなヘルパー関数を次に示します。
;; builds a true/false probability map
(defn tf [true-prob] #(if % true-prob (- 1.0 true-prob)))
上記の関数はクロージャを返します。クロージャは、true
値 (それぞれのfalse
値) を指定すると、イベントの確率X=true
(X
エンコードしている確率変数) を返します。
ネットワークは DAG であるため、循環参照を気にすることなく、ノードを相互に直接参照できます (前述のポインターとまったく同じです)。トポロジー順にグラフを作成するだけです。
(let [gw (node "grass wet" [] (fn [& {:keys [sprinkler rain]}]
(tf (cond (and sprinkler rain) 0.99
sprinkler 0.9
rain 0.8
:else 0.0))))
sk (node "sprinkler" [gw]
(fn [& {:keys [rain]}] (tf (if rain 0.01 0.4))))
rn (node "rain" [sk gw]
(constantly (tf 0.2)))]
(def dag {:nodes {:grass-wet gw :sprinkler sk :rain rn}
:joint (fn [g s r]
(*
(((:table gw) :sprinkler s :rain r) g)
(((:table sk) :rain r) s)
(((:table rn)) r)))}))
各ノードの確率テーブルは、親ノードの状態の関数として与えられ、true
とのfalse
値の確率を返します。例えば、
((:table (:grass-wet dag)) :sprinkler true :rain false)
... 戻ります{:true 0.9, :false 0.09999999999999998}
。
結果として得られる結合関数は、次の式に従って確率を結合します。
P(G,S,R) = P(G|S,R).P(S|R).P(R)
0.0019800000000000004((:joint dag) true true true)
を返します。実際、 によって返される各値は、確率変数の状態を知っている確率を返す((:table <x>) <args>)
の周りのクロージャです。if
各クロージャーをそれぞれのtrue
/false
値で呼び出して、適切な確率を抽出し、それらを乗算します。
ここで、私は少しごまかしています。なぜなら、ジョイント関数はグラフをトラバースして計算する必要があると考えているからです (一般的なケースでは、マクロが役に立ちます)。これはまた、特にノードの状態に関して、少し面倒に感じます。これは、必ずしも true と false だけであるとは限りません。一般的なケースでは、マップを使用する可能性が最も高いでしょう。