cspsat.sat module
SATソルバーのモジュール.
以下のクラスからなる.
SATクラス: SATソルバーのラッパークラス
注釈
本プログラムは学習用の目的で作成されている. 実用上の問題への適用は想定していない.
- class cspsat.sat.SAT(command=None, tempdir=None, cnfFile=None, outFile=None, delete=True, maxClauses=None, limit=None, positiveOnly=False, includeAux=False, verbose=0)[ソース]
ベースクラス:
object
SATソルバーのラッパークラス.
実際のモデル探索には,指定された外部のSATソルバーを使用する. SATソルバーとしては,DIMACS CNFを入力形式とし,SAT solver competitionの出力形式にしたがって結果を標準出力に出力するものなら任意のプログラムを利用できる.
- パラメータ:
command (str, optional) -- 利用するSATソルバーのコマンド.指定しなければ
defaultCommand
を使用する (デフォルト値はWindows系なら"sat4j.bat",その他は"./sat4j").tmpdir (str, optional) -- 一時ファイルのディレクトリ名を指定する.指定しなければシステム設定にしたがう.
cnfFile (str, optional) -- DIMACS CNFファイル名を指定する.指定しなければ一時ファイルが作成される.
outFile (str, optional) -- SATソルバーの出力ファイル名を指定する.指定しなければ一時ファイルが作成される.
delete (bool, optional) -- TrueならSATオブジェクトが削除される際にcnfFile, outFileを削除する (デフォルト値はTrue).
maxClauses (int, optional) -- 節数がこの値を超えたら例外を発生する.指定しなければ
defaultMaxClauses
を使用する (デフォルト値は10000000).limit (int, optional) -- 実行時間の制限秒数を指定する.指定しなければ
defaultLimit
を使用する (デフォルト値は600秒).positiveOnly (bool, optional) -- Trueならモデルに正リテラルのみを含める (デフォルト値はFalse).
includeAux (bool, optional) -- Trueならモデルに補助変数を含める (デフォルト値はFalse).
verbose (int, optional) -- 正ならSATソルバーの情報を表示する (デフォルト値は0).
- variables
変数名のリスト.DIMACS CNFファイル中では,このリスト順に変数番号が付けられている.
- Type:
list
- nclauses
節の個数.
- Type:
int
- model
最後にSATソルバーで求めたモデル (負リテラルも含む).
- Type:
dict
- stats
SATソルバー実行の統計データ.
- Type:
dict
- buffer
DIMACS CNFファイル書き込み用バッファー.
- Type:
bytearray
- bufLimit
DIMACS CNFファイル書き込み用バッファーの最大サイズ (65000バイト).
- Type:
int
サンプル
>>> sat = SAT() # "./sat4j" (Windowsなら"sat4j.bat")をSATソルバーとして利用する >>> p = Bool("p") # 命題変数 p >>> sat.add([ p(1), p(2) ]) # 節 {p(1),p(2)} の追加 >>> sat.solve(num=0) # すべての解 (モデル)を表示する SATISFIABLE Model 1: {p(1): 0, p(2): 1} Model 2: {p(1): 1, p(2): 0} Model 3: {p(1): 1, p(2): 1}
- defaultCommand = './sat4j'
デフォルトのSATソルバーコマンド (Windows系なら"sat4j.bat",その他は"./sat4j").
サンプル
>>> SAT.defaultCommand './sat4j' >>> SAT.defaultCommand = "bin/kissat" # Linuxの場合 >>> SAT.defaultCommand = "wsl bin/kissat" # WindowsでWSLがインストールされている場合
- defaultTempdir = None
一時ファイルのディレクトリ名のデフォルト値.Noneならシステム設定にしたがう.
- defaultLimit = 600
実行時間の制限秒数のデフォルト値 (600秒).
サンプル
>>> SAT.defaultLimit 600 >>> SAT.defaultLimit = 100
- defaultMaxClauses = 10000000
最大節数のデフォルト値 (10000000節).
サンプル
>>> SAT.defaultMaxClauses 10000000 >>> SAT.defaultMaxClauses = 1000000
- nVars()[ソース]
命題変数の個数を返す.
- 戻り値:
命題変数の個数.
サンプル
>>> p = Bool("p") >>> sat = SAT() >>> sat.add([ p(1), p(2) ]) >>> sat.nVars() 2
- nClauses()[ソース]
節の個数を返す.
- 戻り値:
節の個数.
サンプル
>>> p = Bool("p") >>> sat = SAT() >>> sat.add([ p(1), p(2) ]) >>> sat.nClauses() 1
- add(*clauses)[ソース]
複数の節を追加する.
Boolオブジェクトのリストで表現された節を追加する. 命題定数
TRUE
を含む節は追加しない.命題定数FALSE
は節から削除される. 節が文字列の場合は,コメントとみなし何もしない.- パラメータ:
clauses (list of list of Bool) -- 節のリスト.
- 例外:
CspsatException -- 節中のリテラルがBoolオブジェクトでない.
CspsatException -- 節数が合計でmaxClausesを超えた.
サンプル
>>> p = Bool("p") >>> sat = SAT() >>> sat.add([p(0)] , [p(1)]) # 2つの節 {p(0)}, {p(1)} を追加する >>> sat.nClauses() 2
- find()[ソース]
モデルを探索する.
指定されたSATソルバーを用いてモデルを探索し,モデルが見つかればそれを返す.見つからなければNoneを返す. モデルは,キーがBoolオブジェクトで値が0か1の辞書(dict)である.
- 戻り値:
モデル.キーがBoolオブジェクトで値が0か1の辞書(dict)である.モデルが見つからなければNone.
サンプル
>>> p = Bool("p") # 命題変数 p >>> sat = SAT() >>> sat.add([p(1)], [~p(2)]) >>> model = sat.find() # モデルを求める >>> model[p(1)] # 求めたモデルにおける p(1) の値 1 >>> model[p(2)] # 求めたモデルにおける p(2) の値 0
- getStat(includeAll=False)[ソース]
このソルバーによる実行の集計データを返す.
なお,実行時間などの単位は秒で,CPU時間ではなく経過時間である.
- パラメータ:
includeAll (bool, optional) -- Falseならsatフォールドの値は最後のSATソルバー実行の集計データのみである. TrueならすべてのSATソルバー実行の集計データのリストになる (デフォールトはFalse).
- 戻り値:
以下からなる辞書(dict)を返す. * result: 実行結果 (SATISFIABLE, UNSATISFIABLE, TIMEOUT, UNKNOWN, None).複数回のSATソルバーを呼び出しで一度でもSATISFIABLEなら,この値もSATISFIABLEとなる. * ncalls: SATソルバーを呼び出した回数. * nmodels: SATソルバーでモデルを見つけた回数. * time: 関数の実行時間 (秒). * solving: SATソルバーの実行にかかった時間 (累積値,秒). * command: SATソルバーのコマンド. * sat: SATソルバーの集計データ (includeAllがFalseなら最後の集計データ,Trueならすべての集計データのリスト).
SATソルバーの集計データ(satフィールド)は,以下からなるdictである.
result: SATソルバーの実行結果 (SATISFIABLE, UNSATISFIABLE, TIMEOUT, UNKNOWN, None).
variables: CNF式の命題変数の個数.
clauses: CNF式の節の個数.
conflicts: SATソルバー実行での衝突の回数.
decisions: SATソルバー実行での決定の回数.
propagations: SATソルバー実行での伝播の回数.
solving: SATソルバー呼び出しの実行時間 (秒).
サンプル
>>> p = Bool("p") >>> sat = SAT() >>> sat.add([p(1), p(2)]) >>> sat.solve(num=0) SATISFIABLE Model 1: {p(1): 0, p(2): 1} Model 2: {p(1): 1, p(2): 0} Model 3: {p(1): 1, p(2): 1} >>> sat.getStat() # satフィールドの値は,最後のSATソルバー呼び出しのみ {'result': 'SATISFIABLE', 'ncalls': 4, 'nmodels': 3, 'time': 4.027583360671997, 'solving': 4.026149749755859, 'command': None, 'sat': {'result': 'UNSATISFIABLE', 'variables': 2, 'clauses': 4, 'conflicts': 2, 'decisions': 1, 'propagations': 2, 'solving': 1.0061917304992676}} >>> sat.getStat(True) # satフィールドの値は,すべてのSATソルバー呼び出しを含む {'result': 'SATISFIABLE', 'ncalls': 4, 'nmodels': 3, 'time': 4.027583360671997, 'solving': 4.026149749755859, 'command': None, 'sat': [{'result': 'SATISFIABLE', 'variables': 2, 'clauses': 1, 'conflicts': 0, 'decisions': 1, 'propagations': 2, 'solving': 1.0059900283813477}, {'result': 'SATISFIABLE', 'variables': 2, 'clauses': 2, 'conflicts': 1, 'decisions': 2, 'propagations': 3, 'solving': 1.005939245223999}, {'result': 'SATISFIABLE', 'variables': 2, 'clauses': 3, 'conflicts': 1, 'decisions': 1, 'propagations': 3, 'solving': 1.0080287456512451}, {'result': 'UNSATISFIABLE', 'variables': 2, 'clauses': 4, 'conflicts': 2, 'decisions': 1, 'propagations': 2, 'solving': 1.0061917304992676}]}
- addBlock()[ソース]
最後に求めたモデルをブロックする節を追加する.
self.model に保存されている最後に求めたモデルを否定する節を作成し追加する. これにより複数解を求めることが可能になる. なお,補助変数の値の違いは無視される.
サンプル
>>> p = Bool("p") >>> sat = SAT() >>> sat.add([ p(1), p(2) ]) >>> sat.find() {p(1): 0, p(2): 1} >>> sat.addBlock() >>> sat.find() {p(1): 1, p(2): 0} >>> sat.addBlock() >>> sat.find() {p(1): 1, p(2): 1} >>> sat.addBlock() >>> sat.find() # Noneが返る
- commit()[ソース]
現在のDIMACS CNFファイルの状態を返す.
現在のDIMACS CNFファイルの状態 (変数数,節数,ファイルサイズ)を
commitPosition
に保存し,それを返す. そのデータを cancel 関数に渡せば,DIMACS CNFファイルをその状態に戻すことができる.- 戻り値:
現在のDIMACS CNFファイルの状態 (変数数,節数,ファイルサイズ).
サンプル
>>> p = Bool("p") >>> sat = SAT() >>> sat.add([ p(1), p(2) ]) >>> sat.commit() {'variables': 2, 'clauses': 1, 'size': 70}
- cancel(commitPosition=None)[ソース]
DIMACS CNFファイルの状態を戻す.
commit
が返した状態に,DIMACS CNFファイルの状態を戻す. 現在のDIMACS CNFファイルの状態 (変数数,節数,ファイルサイズ)をcommitPosition
に保存し,それを返す. そのデータを cancel 関数に渡せば,DIMACS CNFファイルをその状態に戻すことができる.- パラメータ:
commitPosition (dict, optional) -- DIMACS CNFファイルの状態 (変数数,節数,ファイルサイズ). 指定されていなければ
commitPosition
に保存されている状態を使用する.- 戻り値:
使用したcommitPositionの値.
サンプル
>>> p = Bool("p") >>> sat = SAT() >>> sat.add([ p(1), p(2) ]) >>> sat.commit() {'variables': 2, 'clauses': 1, 'size': 70} >>> sat.add([ ~p(1), ~p(2) ]) >>> sat.solve(num=0) SATISFIABLE Model 1: {p(1): 0, p(2): 1} Model 2: {p(1): 1, p(2): 0} >>> sat.cancel() {'variables': 2, 'clauses': 1, 'size': 70} >>> sat.solve(num=0) SATISFIABLE Model 1: {p(1): 0, p(2): 1} Model 2: {p(1): 1, p(2): 0} Model 3: {p(1): 1, p(2): 1}
- solutions(cnf=None, num=1)[ソース]
複数のモデルを探索し,それらをyieldするジェネレータ関数.
指定された上限の個数まで解(モデル)を探索し,解が見つかればそれをyieldする. 各モデルは,キーがBoolオブジェクトで値が0か1の辞書(dict)である.
複数のモデルを探索する場合は,
addBlock
メソッドを用いて,最後に見つかったモデルを否定する節を追加したのち,SATソルバーを再度起動することで他のモデルを求めている. なお,補助変数の値のみが異なっている場合は同じモデルとみなす.- パラメータ:
cnf (list of list of Bool, optional) -- CNF式 (節のリスト).
num (int, optional) -- 探索するモデルの最大個数.0なら全解を探索する (デフォルト値は1).
- 列挙:
見つかったモデル.キーがBoolオブジェクトで値が0か1の辞書(dict)である.
サンプル
>>> p = Bool("p") >>> sat = SAT() >>> cnf = [ [p(1), p(2)] ] >>> for sol in sat.solutions(cnf, num=0): print(sol) {p(1): 0, p(2): 1} {p(1): 1, p(2): 0} {p(1): 1, p(2): 1}
- solve(cnf=None, num=1, stat=False)[ソース]
複数のモデルを探索し出力する.
solutions
関数で指定された上限の個数まで解(モデル)を探索し,出力する. 出力の最初の行で,SATISFIABLEかUNSATISFIABLEかを表示する.- パラメータ:
cnf (list of list of Bool, optional) -- CNF式 (節のリスト).
num (int, optional) -- 探索するモデルの最大個数.0なら全解を探索する (デフォルト値は1).
stat (bool, optional) -- Trueなら統計データも表示する (デフォルト値はFalse).
サンプル
>>> p = Bool("p") >>> sat = SAT() >>> cnf = [ [p(1), p(2)] ] >>> sat.solve(cnf, num=0, stat=True) # 最後のStatは4つ目のモデルが存在しないことの探索 SATISFIABLE Model 1: {p(1): 0, p(2): 1} Stat: {'result': 'SATISFIABLE', 'ncalls': 1, 'nmodels': 1, 'time': 1.0037319660186768, 'solving': 1.0032446384429932, 'sat': {'result': 'SATISFIABLE', 'variables': 2, 'clauses': 1, 'conflicts': 0, 'decisions': 1, 'propagations': 2, 'solving': 1.0032446384429932}} Model 2: {p(1): 1, p(2): 0} Stat: {'result': 'SATISFIABLE', 'ncalls': 2, 'nmodels': 2, 'time': 2.0091824531555176, 'solving': 2.008450746536255, 'sat': {'result': 'SATISFIABLE', 'variables': 2, 'clauses': 2, 'conflicts': 1, 'decisions': 2, 'propagations': 3, 'solving': 1.0052061080932617}} Model 3: {p(1): 1, p(2): 1} Stat: {'result': 'SATISFIABLE', 'ncalls': 3, 'nmodels': 3, 'time': 3.012281894683838, 'solving': 3.0112452507019043, 'sat': {'result': 'SATISFIABLE', 'variables': 2, 'clauses': 3, 'conflicts': 1, 'decisions': 1, 'propagations': 3, 'solving': 1.0027945041656494}} Stat: {'result': 'SATISFIABLE', 'ncalls': 4, 'nmodels': 3, 'time': 4.0171895027160645, 'solving': 4.01587438583374, 'sat': {'result': 'UNSATISFIABLE', 'variables': 2, 'clauses': 4, 'conflicts': 2, 'decisions': 1, 'propagations': 2, 'solving': 1.004629135131836}}