为 Z3 读取和解析 DIMACS 的更好方法
Better way of reading and parsing DIMACS for Z3
我对 SAT 和 Z3 很陌生(甚至还没有开始使用 SMT)。我一直在玩 gophersat(一个很好的 Go 实现,适用于一组很好的 SAT 问题),我在那里发现了 DIMACS 格式。虽然我同意这不是处理变量的最佳方式,但对于一些简单的测试,我发现它非常方便。我试图检查是否有直接的方法从 Z3 中的 DIMACS 文件读取、解析和构建 SAT 公式。我没有找到任何东西(如前所述,我是新手,所以我可能不知道它的存在)。所以我最终编写了以下代码。我想知道人们对此有何看法,以及是否有更好的方法来实现同样的目标。
(N.B.: 我没有对子句的数量进行任何检查 and/or 公式的变量数量)
from z3 import *
def read_dimacs_and_make_SAT_formula(file_name: str):
vars = dict()
base_name = "x"
with open(file_name, "r") as f:
for idx, line in enumerate(f):
if line.strip() != "\n" and line.strip() != "" and line.strip() != "\r" and line.strip() != None and idx > 0:
splitted_vals = line.split()
for element in splitted_vals:
if int(element) != 0:
if int(element) > 0 and vars.get(element) is None:
# pure variable which never seen
vars[element] = Bool(base_name+element)
elif int(element) > 0 and vars.get("-"+element) is not None:
# pure variable we have seen the negetion before.
vars[element] = Not(vars["-"+element])
elif int(element) < 0 and vars.get("-"+element) is None:
# negetion of a variable and we have not seen it before.
vars[element] = Not(Bool(base_name+element.replace("-", "")))
elif int(element) < 0 and vars.get(element.replace("-", "")) is not None:
# Negetion, we have seen the pure variable before.
vars[element] = Not(vars[element.replace("-", "")])
f.seek(0)
disjunctions = []
for idx, line in enumerate(f):
clauses = []
if line.strip() != "\n" and line.strip() != "" and line.strip() != "\r" and line.strip() != None and idx > 0:
splitted_vals = line.split()
for element in splitted_vals:
if int(element) != 0:
clauses.append(vars[element])
disjunctions.append(Or([*clauses]))
# print(disjunctions)
if disjunctions:
return And([*disjunctions])
return None
使用方法很简单。像这样-
if __name__== "__main__":
s = Solver()
disjunctions = read_dimacs_and_make_SAT_formula("dimacs_files/empty.cnf")
if disjunctions is not None:
s.add(disjunctions)
print(s.check())
if s.check().r != -1:
print(s.model())
如果这样调用,结果如下所示
python test_1.py ✔ │ SAT Py
sat
[x3 = False, x2 = True, x1 = True, x4 = False]
那么问题又来了,你怎么看?我可以做点别的吗?有没有更好的方法?
提前致谢
请注意,代码审查通常与堆栈溢出无关。有一个专门用于此类目的的堆栈交换网站 (https://codereview.stackexchange.com)。您可能会在那里获得有关您的计划的更好反馈。
话虽如此,z3 支持开箱即用的 DIMACS 格式,因此您真的不需要对其进行编程。这是一个例子。假设我们有文件 a.dimacs
:
c simple_v3_c2.cnf
c
p cnf 3 2
1 -3 0
2 3 -1 0
你可以直接将它提供给 z3:
$ z3 a.dimacs
s SATISFIABLE
v -1 -2 -3
或者,如果你想使用Python API,你可以这样写:
from z3 import *
s = Solver()
s.from_file("a.dimacs")
print(s)
print(s.check())
print(s.model())
打印:
[Or(k!1, Not(k!3)), Or(Not(k!1), k!2, k!3)]
sat
[k!1 = False, k!3 = False, k!2 = False]
请注意,文件扩展名必须是 dimacs
,因此当您调用 s.from_file
时,z3 将在内部使用正确的解析器。从命令行,您可以使用 .dimacs
扩展,或将命令行选项 -dimacs
传递给 z3.
我对 SAT 和 Z3 很陌生(甚至还没有开始使用 SMT)。我一直在玩 gophersat(一个很好的 Go 实现,适用于一组很好的 SAT 问题),我在那里发现了 DIMACS 格式。虽然我同意这不是处理变量的最佳方式,但对于一些简单的测试,我发现它非常方便。我试图检查是否有直接的方法从 Z3 中的 DIMACS 文件读取、解析和构建 SAT 公式。我没有找到任何东西(如前所述,我是新手,所以我可能不知道它的存在)。所以我最终编写了以下代码。我想知道人们对此有何看法,以及是否有更好的方法来实现同样的目标。
(N.B.: 我没有对子句的数量进行任何检查 and/or 公式的变量数量)
from z3 import *
def read_dimacs_and_make_SAT_formula(file_name: str):
vars = dict()
base_name = "x"
with open(file_name, "r") as f:
for idx, line in enumerate(f):
if line.strip() != "\n" and line.strip() != "" and line.strip() != "\r" and line.strip() != None and idx > 0:
splitted_vals = line.split()
for element in splitted_vals:
if int(element) != 0:
if int(element) > 0 and vars.get(element) is None:
# pure variable which never seen
vars[element] = Bool(base_name+element)
elif int(element) > 0 and vars.get("-"+element) is not None:
# pure variable we have seen the negetion before.
vars[element] = Not(vars["-"+element])
elif int(element) < 0 and vars.get("-"+element) is None:
# negetion of a variable and we have not seen it before.
vars[element] = Not(Bool(base_name+element.replace("-", "")))
elif int(element) < 0 and vars.get(element.replace("-", "")) is not None:
# Negetion, we have seen the pure variable before.
vars[element] = Not(vars[element.replace("-", "")])
f.seek(0)
disjunctions = []
for idx, line in enumerate(f):
clauses = []
if line.strip() != "\n" and line.strip() != "" and line.strip() != "\r" and line.strip() != None and idx > 0:
splitted_vals = line.split()
for element in splitted_vals:
if int(element) != 0:
clauses.append(vars[element])
disjunctions.append(Or([*clauses]))
# print(disjunctions)
if disjunctions:
return And([*disjunctions])
return None
使用方法很简单。像这样-
if __name__== "__main__":
s = Solver()
disjunctions = read_dimacs_and_make_SAT_formula("dimacs_files/empty.cnf")
if disjunctions is not None:
s.add(disjunctions)
print(s.check())
if s.check().r != -1:
print(s.model())
如果这样调用,结果如下所示
python test_1.py ✔ │ SAT Py
sat
[x3 = False, x2 = True, x1 = True, x4 = False]
那么问题又来了,你怎么看?我可以做点别的吗?有没有更好的方法?
提前致谢
请注意,代码审查通常与堆栈溢出无关。有一个专门用于此类目的的堆栈交换网站 (https://codereview.stackexchange.com)。您可能会在那里获得有关您的计划的更好反馈。
话虽如此,z3 支持开箱即用的 DIMACS 格式,因此您真的不需要对其进行编程。这是一个例子。假设我们有文件 a.dimacs
:
c simple_v3_c2.cnf
c
p cnf 3 2
1 -3 0
2 3 -1 0
你可以直接将它提供给 z3:
$ z3 a.dimacs
s SATISFIABLE
v -1 -2 -3
或者,如果你想使用Python API,你可以这样写:
from z3 import *
s = Solver()
s.from_file("a.dimacs")
print(s)
print(s.check())
print(s.model())
打印:
[Or(k!1, Not(k!3)), Or(Not(k!1), k!2, k!3)]
sat
[k!1 = False, k!3 = False, k!2 = False]
请注意,文件扩展名必须是 dimacs
,因此当您调用 s.from_file
时,z3 将在内部使用正确的解析器。从命令行,您可以使用 .dimacs
扩展,或将命令行选项 -dimacs
传递给 z3.