充足可能性問題について
新山 祐介
権藤研 輪講 2018/6/19

メタ背景知識

構成

  1. SATソルバとは何か? どう役に立つのか?
  2. 手法1 - バックトラックによる解法。
  3. 手法2 - Resolutionによる解法。
  4. 手法3 - 節の学習による解法。
  5. SATソルバとSMTソルバの違い。
SATソルバって何。

SATソルバとは何か?

例題 1

例題 1 (つづき)

例題 1 (つづき)

例題 2

例題 2 (つづき)

Tseytin Encoding

CNF式
X = 1X
X = 0X
Y = X(X∨Y) ∧ (X∨Y)
Y = X(X∨Y) ∧ (XY)
Z = X ∧ Y(X∨Z) ∧ (Y∨Z) ∧ (XY∨Z)
Z = X ∨ Y(X∨Z) ∧ (Y∨Z) ∧ (X∨Y∨Z)
Z = X ⊕ Y(X∨Y∨Z) ∧ (X∨Y∨Z) ∧ (X∨Y∨Z) ∧ (XYZ)
SATソルバの作り方。
手法1 - バックトラック

SATソルバ - アホアホ版

SATソルバの効率化

DP (David-Patnum) 法

decisions = []    # 決めた値
implications = [] # 推論した値

while 未定義の変数がある:
    decide()      # ひとつ決める
    bcp()         # 推論する
    if 失敗:
        undo()    # もとに戻す
        flip()    # 別の選択肢を試す
        if 失敗:
            return UNSAT  # もう試すものがない
return SAT

(a ∨ b) ∧ (bc)
  1. a=1 を決定 (decide)。
  2. b=1 を推論 (bcp)。
  3. c=0 を推論 (bcp)。
  4. 成功。(SAT)

(a ∨ b ∨ c) ∧ (bc) ∧ (ab ∨ c)
  1. a=1 を決定 (decide)。
  2. b=1 を決定 (decide)。
  3. c=0 を推論 (bcp)。
  4. (ab ∨ c) が充足不能 (bcp 失敗)。
  5. c を未定義に戻す (undo)。
  6. b=1 → b=0 にする (flip)。
  7. c=1 を決定 (decide)。
  8. 成功。(SAT)

関数 decide()

# 未定義の変数の値をひとつ決める
def decide():
    for v in 各変数:
        if v == 未定義:
           decisions.append((v, 1))
           break

関数 bcp()

# 推論して値を決める
def bcp():
    for C in 各CNF節:
        if Cはすでに充足している:
            continue
        # Cはまだ充足していない。
        U = [ C中の変数で、未定義なもの ]
        if len(U) == 0:    # 変更の余地なし
            return 失敗
        elif len(U) == 1:  # テンパってる
            implications.append((v, 0または1))
    return 成功

実際の実装

while level < N:   # level = 現在の段階
    decide(level)   # ひとつ決める
    bcp(level)      # 推論する
    if 成功:
        level += 1  # 次の段階へ
    else:
        level = まだ選択肢がある段階
        undo(level)  # その状態に戻す
        flip(level)  # 別の選択肢を試す
        if 失敗:
            return UNSAT
return SAT

Chaff [1]の実装

「見張り」を用いたBCPの高速化

SATソルバの作り方。
手法2 - Resolution

Resolutionとは何か?

Resolutionの例

Resolutionは速いのか?

DP法との類似点

Refutation Tree の例

SATソルバの作り方。
手法3 - 学習する

CDCL法

# Conflict Driven Clause Learning 法
decisions = []    # 決めた値
implications = [] # 推論した値

while 未定義の変数がある:
    decide()      # ひとつ決める
    bcp()         # 推論する
    if 失敗:
learn(decisions) # 失敗から学ぶ
undo() # もとに戻す flip() # 別の選択肢を試す if 失敗: return UNSAT # もう試すものがない return SAT

CDCL法の原理

  1. たとえば a=0, b=1, c=1 と決めた (decide) とする。
  2. 失敗した。(;_;)
  3. (a ∧ b ∧ c) は正しくないことが判明した。
  4. (a ∧ b ∧ c) の否定 =
    (a ∨ bc) が正しいという事実を学習した。
  5. 節 (a ∨ bc) を 既存のCNF式の列に追加する。
    → 「転んでもただでは起きない」ソルバ。

その他いろいろな工夫 (Chaff)

SATソルバとSMTソルバ。

SATソルバ → SMTソルバ

例題 3

Z3を使ってみる

まとめ。

結論

TAoCP = 教科書としてはダメ。