SAT ソルバー具体例集

抽象的な定義だけでは、DPLL と CDCL の理解は進みにくい。ここでは、CNF の意味、典型的な符号化、Tseitin 変換、DPLL の探索、CDCL の学習、2-watched literals の更新を、手を動かして確認できる具体例としてまとめる。

45+ examplesCNF evaluatorDPLL traceCDCL traceTseitin encodings

0. 使い方

このレポートは、理論説明ではなく「例を読む」ための文書である。まず CNF 評価器で節の状態を観察し、その後、具体例バンクをカテゴリで絞り込み、最後に DPLL/CDCL のトレースをステップごとに追うと理解しやすい。

評価
割当の下で節が true / false / unit / unresolved のどれになるかを見る。
符号化
命題、制約、グラフ問題を CNF に落とす定石を見る。
探索
DPLL と CDCL が同じ式に対してどう異なる振る舞いをするかを見る。

1. 記法の確認

記号意味
リテラル変数または変数の否定\(x\), \(\neg x\)
リテラルの OR\((x\lor \neg y\lor z)\)
CNF節の AND\((x\lor y)\land(\neg x\lor z)\)
単位節未割当リテラルが 1 個だけ残った節\((\bot\lor \bot\lor z)\) は \((z)\)
空節全リテラルが偽になった節\(()\), contradiction
部分割当で節を見るときは、節を「真」「偽」「単位」「未決定」の 4 状態に分類する。この 4 状態が BCP と衝突検出の基礎である。

2. インタラクティブ CNF 評価器

式を選び、変数ボタンを押して \(U\to T\to F\to U\) と切り替える。各節が満たされたか、単位になったか、衝突したかを確認できる。


T=true, F=false, U=未割当。

3. 大量具体例バンク

検索欄とカテゴリで絞り込める。各例は、SAT ソルバーが実際に扱う「構造」を意識している。

例 1:空 CNF は真

意味論
\(F=\bigwedge_{C\in\varnothing}C\)

AND の空積なので任意の割当で真。SAT ソルバーでは、節が 0 個なら即 SAT と判定できる。

例 2:空節を含む CNF は偽

意味論
\(F=()\land(x\lor y)\)

空節は OR の空和で偽。どんな割当でも空節は満たされないため UNSAT。

例 3:節の評価

意味論
\(C=(x\lor\neg y\lor z),\quad x=F,y=T,z=U\)

\(x\) は偽、\(\neg y\) も偽、\(z\) は未割当。したがって節は単位で、\(z=T\) が強制される。

例 4:節が既に満たされた場合

意味論
\(C=(x\lor\neg y\lor z),\quad x=T\)

他のリテラルの値に関係なく節は真。DPLL/CDCL は満たされた節から新たな伝播を行わない。

例 5:未決定節

意味論
\(C=(x\lor y\lor z),\quad x=F,y=U,z=U\)

未割当が 2 個あるので単位ではない。伝播は起きず、探索ヒューリスティックが次の決定を行う。

例 6:含意の CNF

符号化
\(a\Rightarrow b\equiv \neg a\lor b\)

「a ならば b」は、a が真で b が偽の場合だけ禁止する 1 節で表せる。

例 7:双方向含意

符号化
\(a\leftrightarrow b\equiv(\neg a\lor b)\land(a\lor\neg b)\)

両方向の含意を AND する。等価制約はゲート符号化の基本である。

例 8:OR ゲート

符号化★★
\(z\leftrightarrow(a\lor b)\)

CNF は \((\neg z\lor a\lor b)\land(z\lor\neg a)\land(z\lor\neg b)\)。最初の節は \(z\Rightarrow a\lor b\)、後二者は \(a\Rightarrow z\), \(b\Rightarrow z\)。

例 9:AND ゲート

符号化★★
\(z\leftrightarrow(a\land b)\)

CNF は \((\neg z\lor a)\land(\neg z\lor b)\land(z\lor\neg a\lor\neg b)\)。

例 10:NOT ゲート

符号化★★
\(z\leftrightarrow\neg a\)

CNF は \((\neg z\lor\neg a)\land(z\lor a)\)。これは \(z\) と \(a\) が反対値になることを表す。

例 11:XOR ゲート

符号化★★★
\(z\leftrightarrow(a\oplus b)\)

CNF は \((\neg a\lor\neg b\lor\neg z)\land(a\lor b\lor\neg z)\land(a\lor\neg b\lor z)\land(\neg a\lor b\lor z)\)。4 つの禁止パターンを節にしたもの。

例 12:少なくとも 1 つ

符号化
\(x_1\lor x_2\lor x_3\lor x_4\)

「少なくとも 1 つ真」はそのまま 1 つの長い節になる。

例 13:高々 1 つ

符号化★★
\(\bigwedge_{i

ペアごとに「同時に真ではない」を課す。\(n\) 変数なら \(\binom n2\) 節になる。

例 14:ちょうど 1 つ

符号化★★
\((x_1\lor\cdots\lor x_n)\land\bigwedge_{i

「少なくとも 1 つ」と「高々 1 つ」を組み合わせる。

例 15:三角形グラフの 3 彩色

符号化★★★
\(v\in\{A,B,C\},\; color\in\{R,G,B\}\)

各頂点に exactly-one 色制約を置き、各辺 \((u,v)\) と色 \(c\) に対して \((\neg x_{u,c}\lor\neg x_{v,c})\) を置く。三角形は 3 色で SAT。

例 16:鳩の巣原理 PHP(3,2)

符号化★★★
\(p_{i,h}=\) 鳩 \(i\) が穴 \(h\) に入る

各鳩は少なくとも 1 つの穴に入る。各穴に 2 羽は入れない。3 羽を 2 穴に入れる制約は UNSAT。

例 17:Tseitin の基本

Tseitin★★
\(F=(a\land b)\lor c\)

補助変数 \(u\leftrightarrow a\land b\), \(z\leftrightarrow u\lor c\) を導入し、最後に単位節 \((z)\) を加える。

例 18:入れ子 OR/AND

Tseitin★★
\(F=(a\lor b)\land(c\lor d)\)

\(u\leftrightarrow a\lor b\), \(v\leftrightarrow c\lor d\), \(z\leftrightarrow u\land v\), \((z)\)。節数は式サイズに線形。

例 19:否定を含むゲート

Tseitin★★
\(F=\neg(a\land b)\)

\(u\leftrightarrow a\land b\), \(z\leftrightarrow\neg u\), \((z)\)。直接展開すれば \((\neg a\lor\neg b)\) だが、一般式では Tseitin が爆発を防ぐ。

例 20:分配展開の爆発回避

Tseitin★★★
\((a_1\land b_1)\lor\cdots\lor(a_k\land b_k)\)

素朴に CNF へ分配すると節数が指数的に増える場合がある。Tseitin なら各 AND に補助変数を置き、最後を OR ゲートとして線形サイズで表せる。

例 21:同充足性と同値性の違い

Tseitin★★★
\(F\) と \(T(F)\)

Tseitin 変換は補助変数を導入するため、元変数だけで見れば同じモデル射影を持つが、全変数上で完全に同じ式ではない。重要なのは satisfiability を保存すること。

例 22:単位伝播チェーン

DPLL
\((a)\land(\neg a\lor b)\land(\neg b\lor c)\)

\((a)\) から \(a=T\)、次に \(b=T\)、次に \(c=T\)。決定なしで SAT まで進む。

例 23:即時衝突

DPLL
\((a)\land(\neg a)\)

単位伝播で \(a=T\) と \(a=F\) が同時に要求される。決定レベル 0 の衝突なので UNSAT。

例 24:純リテラル

DPLL★★
\((a\lor b)\land(a\lor\neg c)\land(b\lor c)\)

\(a\) が正でしか現れないので、古典 DPLL では \(a=T\) と置けば \(a\) を含む節を満たせる。現代 CDCL は純リテラル規則を常用しないことが多い。

例 25:分岐後に伝播で SAT

DPLL★★
\((a\lor b)\land(\neg a\lor c)\land(\neg b\lor\neg c)\)

\(a=T\) と決定すると \((\neg a\lor c)\) から \(c=T\)。さらに \((\neg b\lor\neg c)\) から \(b=F\)。全節が満たされる。

例 26:両枝衝突による UNSAT

DPLL★★
\((a\lor b)\land(a\lor\neg b)\land(\neg a\lor b)\land(\neg a\lor\neg b)\)

\(a=T\) では \(b=T\) と \(b=F\) が同時に強制される。\(a=F\) でも同様。したがって UNSAT。

例 27:決定順序で探索量が変わる

DPLL★★★
\((x\lor y)\land(\neg x\lor y)\land(\neg y\lor z)\)

先に \(y\) を決めると速い。\(y=T\) なら前 2 節は満たされ、\((\neg y\lor z)\) から \(z=T\)。変数選択は性能に直結する。

例 28:watch の初期化

watched literals★★
\(C=(a\lor b\lor c\lor d)\)

任意の 2 つ、たとえば \(a,b\) を watch する。未割当ならどちらも偽でないため不変条件を満たす。

例 29:watch 移動

watched literals★★
\(C=(a\lor b\lor c),\quad a=F\)

\(a\) が watch されていたら、未割当の \(c\) へ watch を移動できる。伝播は起きない。

例 30:watch から単位伝播

watched literals★★
\(C=(a\lor b\lor c),\quad a=F,b=F,c=U\)

代替 watch がなく、他方の watch が \(c\) なら \(c=T\) が強制される。

例 31:watch から衝突

watched literals★★
\(C=(a\lor b),\quad a=F,b=F\)

両 watch が偽で、代替もない。節が空になり衝突する。

例 32:理由節

CDCL★★
\((\neg a\lor b),\quad a=T\)

節が単位になり \(b=T\) を伝播する。このとき \(b\) の reason は節 \((\neg a\lor b)\)。

例 33:線形含意からの学習

CDCL★★
\((\neg a\lor b)\land(\neg b\lor c)\land(\neg c\lor d)\land(\neg d\lor\neg a)\)

\(a=T\) と決めると \(b,c,d\) が伝播し \((\neg d\lor\neg a)\) と衝突。resolution により学習節 \((\neg a)\) を得る。

例 34:1-UIP 節

CDCL★★★
現在レベルの衝突節に複数リテラルがある

衝突節中の現在レベルリテラルが 2 個以上なら、トレイル末尾から理由節で resolution する。現在レベルリテラルが 1 個になった節が asserting clause。

例 35:backjump レベル

CDCL★★★
\(L=(\neg u@7\lor p@2\lor q@4)\)

現在レベル 7 のリテラルは 1 個。残りの最大レベルは 4。したがって level 4 へ戻り、\(\neg u\) を単位伝播する。

例 36:決定レベル 0 衝突

CDCL★★★
BCP at level 0 で空節

レベル 0 には戻るべき決定がない。したがって式そのものが UNSAT と確定する。

例 37:restart の意味

CDCL★★★
trail を消すが learned clauses は残す

restart は探索木を根に戻す操作だが、学習節により同じ悪い領域に入りにくくなる。完全性を壊さない。

例 38:DIMACS の 1 節

実装
1 -3 4 0

これは節 \((x_1\lor\neg x_3\lor x_4)\) を表す。0 は節の終端。

例 39:trail entry

実装★★
{ lit: x7, level: 3, reason: C42 }

割当リテラル、決定レベル、理由節を保存する。決定リテラルなら reason は null。

例 40:watch list

実装★★
watch[¬x] = [C5, C18, C22]

\(x=T\) にしたとき偽になるリテラルは \(\neg x\)。その watch list だけを走査する。

例 41:VSIDS bump

実装★★★

衝突解析に出た変数の活動度を増やす。最近の衝突に関わる変数を優先して決定するため、探索が衝突の核心へ集中する。

例 42:LBD

実装★★★
\(L=(a@2\lor b@2\lor c@7\lor d@9)\)

この学習節の LBD は distinct decision levels の数なので 3。LBD が小さい節は複数レベルを強く結び、残す価値が高いとみなされやすい。

例 43:節削減

実装★★★

学習節を無制限に溜めると BCP が重くなる。活動度や LBD を使って、役に立ちにくい学習節を定期的に削除する。

例 44:phase saving

実装★★

変数を再び決定するとき、前回使った極性を優先する。restart 後に近い探索状態を再構築しやすい。

例 45:incremental SAT

実装★★★

同じ基礎 CNF に異なる仮定リテラルを加えて何度も解く。SMT、モデル検査、最適化で重要になる。

4. DPLL/CDCL トレース再生

DPLL トレース:両枝衝突

CDCL トレース:線形含意から \((\neg a)\) を学習

5. 例から抽出するパターン

単位伝播は局所的
節が単位になった瞬間だけ新しい割当が強制される。DPLL/CDCL の速度はこの検出に依存する。
学習節は否定原因
衝突を起こした割当集合 \(A\) を同時に禁止するため、学習節は \(\bigvee_{a\in A}\neg a\) の形になる。
実装は「見る節を減らす」
2-watched literals、restart、節削減、活動度はすべて、膨大な探索空間の中で注目すべき場所を絞る技術である。