我正在尝试制作一个Z3程序(在Python中),该程序会生成执行某些任务(例如,将两个n位数字相加)的布尔电路,但其性能非常糟糕,以至于无法对整个对象进行强力搜索解决方案空间会更快。这是我第一次使用Z3,所以我可能会做一些会影响性能的事情,但是我的代码看起来还不错。
以下是从我的代码here中复制的:
from z3 import *
BITLEN = 1 # Number of bits in input
STEPS = 1 # How many steps to take (e.g. time)
WIDTH = 2 # How many operations/values can be stored in parallel, has to be at least BITLEN * #inputs
# Input variables
x = BitVec('x', BITLEN)
y = BitVec('y', BITLEN)
# Define operations used
op_list = [BitVecRef.__and__, BitVecRef.__or__, BitVecRef.__xor__, BitVecRef.__xor__]
unary_op_list = [BitVecRef.__invert__]
for uop in unary_op_list:
op_list.append(lambda x, y : uop(x))
# Chooses a function to use by setting all others to 0
def chooseFunc(i, x, y):
res = 0
for ind, op in enumerate(op_list):
res = res + (ind == i) * op(x, y)
return res
s = Solver()
steps = []
# First step is just the bits of the input padded with constants
firststep = Array("firststep", IntSort(), BitVecSort(1))
for i in range(BITLEN):
firststep = Store(firststep, i * 2, Extract(i, i, x))
firststep = Store(firststep, i * 2 + 1, Extract(i, i, y))
for i in range(BITLEN * 2, WIDTH):
firststep = Store(firststep, i, BitVec("const_0_%d" % i, 1))
steps.append(firststep)
# Generate remaining steps
for i in range(1, STEPS + 1):
this_step = Array("step_%d" % i, IntSort(), BitVecSort(1))
last_step = steps[-1]
for j in range(WIDTH):
func_ind = Int("func_%d_%d" % (i,j))
s.add(func_ind >= 0, func_ind < len(op_list))
x_ind = Int("x_%d_%d" % (i,j))
s.add(x_ind >= 0, x_ind < WIDTH)
y_ind = Int("y_%d_%d" % (i,j))
s.add(y_ind >= 0, y_ind < WIDTH)
node = chooseFunc(func_ind, Select(last_step, x_ind), Select(last_step, y_ind))
this_step = Store(this_step, j, node)
steps.append(this_step)
# Set the result to the first BITLEN bits of the last step
if BITLEN == 1:
result = Select(steps[-1], 0)
else:
result = Concat(*[Select(steps[-1], i) for i in range(BITLEN)])
# Set goal
goal = x | y
s.add(ForAll([x, y], goal == result))
print(s)
print(s.check())
print(s.model())
代码基本上将输入布置为单个位,然后在每个“步骤”中,五个布尔函数之一可以对上一步中的值进行操作,其中最后一步表示最终结果。
在此示例中,我生成了一个电路来计算两个1位输入的布尔“或”,并且电路中提供了“或”功能,因此解决方案很简单。
我的解决方案空间只有5 * 5 * 2 * 2 * 2 * 2 = 400:
此代码需要花费几秒钟的时间运行并提供正确的答案,但是我认为它应该立即运行,因为只有400种可能的解决方案,其中很多是有效的。如果我将输入增加到两位长,则解决方案空间的大小为(5 ^ 4)*(4 ^ 8)= 40,960,000,并且永远不会在计算机上完成,尽管我觉得Z3应该很容易做到。 / p>
我也有效地尝试了相同的代码,但用Arrays / Store / Select替换了Python列表,并使用与selectFunc()中相同的技巧“选择”了变量。该代码为here,它的运行时间与原始代码几乎相同,因此不会加速。
我在做会大大减慢求解器速度的事情吗?谢谢!
答案 0 :(得分:1)
您的__xor__
中有一个重复的op_list
;但这并不是主要问题。随着位大小的增加,速度变慢是不可避免的,但是乍一看,您可以(并且应该)在此处避免将整数推理与布尔值混合。我会将您的chooseFunc
编写如下:
def chooseFunc(i, x, y):
res = False;
for ind, op in enumerate(op_list):
res = If(ind == i, op (x, y), res)
return res
查看这是否以任何有意义的方式改善了运行时间。如果没有,那么下一步就是尽可能多地删除数组。