如何在Z3Py中将变量声明为数组索引?

时间:2017-01-17 22:54:30

标签: variables optimization constraints z3 z3py

假设x,y,z是int变量而A是矩阵,我想表达一个约束:

z == A[x][y]

然而,这会导致错误: TypeError:object不能解释为索引

这样做的正确方法是什么?

=======================

一个具体的例子:

我想选择2项具有最佳组合得分的项目, 其中得分由每个项目的值和选择对上的奖励给出。 例如, 对于3个项目:a,b,c具有相关值[1,2,1],并且对上的奖励(a,b)= 2,(a,c)= 5,(b,c)= 3,最佳选择是(a,c),因为它得分最高:1 + 1 + 5 = 7.

我的问题是如何表示选择奖金的约束。 假设CHOICE [0]和CHOICE [1]是选择变量而B是奖励变量。 理想的约束应该是:

B = bonus[CHOICE[0]][CHOICE[1]]

但它导致TypeError:object无法解释为索引 我知道另一种方法是使用嵌套for来首先实例化CHOICE,然后代表B,但这对于大量数据来说效率非常低。 有没有专家建议我提供更好的解决方案呢?

如果有人想玩玩具示例,请输入以下代码:

from z3 import *

items = [0,1,2]
value = [1,2,1]
bonus = [[1,2,5],
         [2,1,3],
         [5,3,1]]
choices = [0,1]
# selection score
SCORE = [ Int('SCORE_%s' % i) for i in choices ]

# bonus
B = Int('B')

# final score
metric = Int('metric')

# selection variable
CHOICE = [ Int('CHOICE_%s' % i) for i in choices ]

# variable domain
domain_choice = [ And(0 <= CHOICE[i], CHOICE[i] < len(items))  for i in choices ]

# selection implication
constraint_sel = []
for c in choices:
    for i in items:
        constraint_sel += [Implies(CHOICE[c] == i, SCORE[c] == value[i])]

# choice not the same
constraint_neq = [CHOICE[0] != CHOICE[1]]

# bonus constraint. uncomment it to see the issue
# constraint_b = [B == bonus[val(CHOICE[0])][val(CHOICE[1])]]

# metric definition
constraint_sumscore = [metric == sum([SCORE[i] for i in choices ]) + B]

constraints = constraint_sumscore + constraint_sel + domain_choice + constraint_neq + constraint_b

opt = Optimize()
opt.add(constraints)
opt.maximize(metric)

s = []
if opt.check() == sat:
    m = opt.model()
    print [ m.evaluate(CHOICE[i]) for i in choices ]
    print m.evaluate(metric)
else:
    print "failed to solve"

1 个答案:

答案 0 :(得分:2)

原来解决这个问题的最好方法是实际上根本不使用数组,而只是创建整数变量。使用这种方法,最初发布的317x317项目问题实际上在我相对较旧的计算机上大约40秒内得到解决:

[ 0.01s] Data loaded
[ 2.06s] Variables defined
[37.90s] Constraints added
[38.95s] Solved:
 c0     = 19
 c1     = 99
 maxVal = 27

请注意实际的&#34;解决方案&#34;发现大约一秒钟!但是,添加所有必需的约束需要花费40秒的大部分时间。这是编码:

from z3 import *
import sys
import json
import sys
import time

start = time.time()

def tprint(s):
    global start
    now = time.time()
    etime = now - start
    print "[%ss] %s" % ('{0:5.2f}'.format(etime), s)

# load data
with open('data.json') as data_file:
    dic = json.load(data_file)
tprint("Data loaded")

items     = dic['items']
valueVals = dic['value']
bonusVals = dic['bonusVals']

vals = [[Int("val_%d_%d" %  (i, j)) for j in items if j > i] for i in items]
tprint("Variables defined")

opt = Optimize()
for i in items:
    for j in items:
      if j > i:
         opt.add(vals[i][j-i-1] == valueVals[i] + valueVals[j] + bonusVals[i][j])

c0, c1 = Ints('c0 c1')
maxVal = Int('maxVal')

opt.add(Or([Or([And(c0 == i, c1 == j, maxVal == vals[i][j-i-1]) for j in items if j > i]) for i in items]))
tprint("Constraints added")

opt.maximize(maxVal)

r = opt.check ()
if r == unsat or r == unknown:
   raise Z3Exception("Failed")
tprint("Solved:")
m = opt.model()
print " c0     = %s" % m[c0]
print " c1     = %s" % m[c1]
print " maxVal = %s" % m[maxVal]

我认为这和Z3在这个问题上的速度一样快。当然,如果您想最大化多个指标,那么您可以构建代码,以便重用大多数约束,从而分摊构建模型的成本一次,然后逐步优化为了获得最佳性能。