How to declare constraints with variable as array index in Z3Py?

江枫思渺然 提交于 2019-12-12 02:08:22

问题


Suppose x,y,z are int variables and A is a matrix, I want to express a constraint like:

z == A[x][y]

However this leads to an error: TypeError: object cannot be interpreted as an index

What would be the correct way to do this?

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

A specific example:

I want to select 2 items with the best combination score, where the score is given by the value of each item and a bonus on the selection pair. For example, for 3 items: a, b, c with related value [1,2,1], and the bonus on pairs (a,b) = 2, (a,c)=5, (b,c) = 3, the best selection is (a,c), because it has the highest score: 1 + 1 + 5 = 7.

My question is how to represent the constraint of selection bonus. Suppose CHOICE[0] and CHOICE[1] are the selection variables and B is the bonus variable. The ideal constraint should be:

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

but it results in TypeError: object cannot be interpreted as an index I know another way is to use a nested for to instantiate first the CHOICE, then represent B, but this is really inefficient for large quantity of data. Could any expert suggest me a better solution please?

If someone wants to play a toy example, here's the code:

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:


Turns out the best way to deal with this problem is to actually not use arrays at all, but simply create integer variables. With this method, the 317x317 item problem originally posted actually gets solved in about 40 seconds on my relatively old computer:

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

Note that the actual "solution" is found in about a second! But adding all the required constraints takes the bulk of the 40 seconds spent. Here's the encoding:

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]

I think this is as fast as it'll get with Z3 for this problem. Of course, if you want to maximize multiple metrics, then you can probably structure the code so that you can reuse most of the constraints, thus amortizing the cost of constructing the model just once, and incrementally optimizing afterwards for optimal performance.



来源:https://stackoverflow.com/questions/41708500/how-to-declare-constraints-with-variable-as-array-index-in-z3py

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!