[英]z3py: Symmetry breaking constraint by lexicographic order
我有兩個整數變量數組(它們代表二維空間坐標),一個是另一個的對稱配置。 為了打破對稱性,我想檢查第一個數組描述的找到的解決方案(我們稱之為 P)是否是關於對稱解決方案的字典順序(所以我可以丟棄對稱解決方案)。 無論如何,我在編寫一個檢查字典順序的函數時遇到了麻煩:
到目前為止(但它是不正確的),我想出了這個:
from z3 import *
from math import ceil
width = 8
blocks = 4
x = [3, 3, 5, 5]
y = [3, 5, 3, 5]
# [blocks x 2] list of integer variables
P = [[Int("x_%s" % (i + 1)), Int("y_%s" % (i + 1))]
for i in range(blocks)]
# value/ domain constraint
values = [And(0 <= P[i][0], P[i][0] + x[i] <= width, 0 <= P[i][1]) # , P[i][1] + y[i] <= height)
for i in range(blocks)]
# no overlap constraint
no_overlap = [Or(i == j, # indices must be different
Or(P[j][0] + x[j] <= P[i][0], # left
P[j][0] >= P[i][0] + x[i], # right
P[j][1] + y[j] <= P[i][1], # down
P[j][1] >= P[i][1] + y[i] # up
))
for j in range(blocks) for i in range(blocks)]
# Return maximum of a vector; error if empty
def symMax(vs):
maximum = vs[0]
for value in vs[1:]:
maximum = If(value > maximum, value, maximum)
return maximum
obj = symMax([P[i][1] + y[i] for i in range(blocks)])
def symmetric(on_x, on_y):
if on_x and on_y:
return [[width - P[block][0] - x[block], obj - P[block][1] - y[block]] for block in range(blocks)]
elif on_x:
return [[width - P[block][0] - x[block], P[block][1]] for block in range(blocks)]
elif on_y:
return [[P[block][0], obj - P[block][1] - y[block]] for block in range(blocks)]
#function I want to emulate
def lexicographic_order(a1, a2):
for a, b in zip(a1, a2):
if a < b:
return True
if a > b:
return False
return True
# my attempt: raise error "Z3 AST expected"
def lexSymb(a1, a2):
for a, b in zip(a1, a2):
tmp = If(a < b, True, False)
if tmp.eq(True):
return True
tmp = If(a > b, False, True)
if tmp.eq(False):
return False
return True
lex_x = lexSymb1(P, symmetric(True, False))
lex_y = lexSymb1(P, symmetric(False, True))
lex_xy = lexSymb1(P, symmetric(True, True))
board_problem = values + no_overlap + lex_x + lex_y + lex_xy
o = Optimize()
o.add(board_problem)
o.minimize(obj)
if o.check() == sat:
print("Solved")
elif o.check() == unsat:
print("The problem is unsatisfiable")
else:
print("Unexpected behaviour")
讓我知道是否有可能以與符號表達式一起使用的方式修改“lexicographic_order()”,或者是否有另一種我沒有見過的方法。
你說你想要一個符號版本:
def lexicographic_order(a1, a2):
for a, b in zip(a1, a2):
if a < b:
return True
if a > b:
return False
return True
我不認為這是你想要的。 為什么? 請注意,在 Python 中, return
立即退出該函數。 所以,這只會比較這些列表的第一個元素(假設它們是非空的),並立即返回。 我不認為這是你想要的。
字典排序通常意味着您必須比較整個列表。 這是您符號化編碼的方式:
# Does a1 come before a2 lexicographically?
# We assume a1 != a2. If same, the algorithm will produce True.
def precedes(a1, a2):
if not a1:
return True
if not a2:
return False
return Or(a1[0] < a2[0], And(a1[0] == a2[0], precedes(a1[1:], a2[1:])))
看看這個函數是如何工作的,很有啟發性。 首先是一個具體的值:
> print(precedes([1,2,3],[4,5,6]))
Or(True,
And(False,
Or(True, And(False, Or(True, And(False, True))))))
請注意,如果第一個列表在第二個列表之前按字典順序排列,我們會構建“計算”的 AST。 上面的表達式簡化為True
因為您可以輕松驗證自己,但我們不計算True
,我們構建的表達式的值為True
。 當我們在符號值上運行它時,這種結構的重要性變得顯而易見:
> print(precedes([Int('a'), Int('b')], [Int('c'), Int('d')]))
Or(a < c, And(a == c, Or(b < d, And(b == d, True))))
現在您將看到該值是如何“符號化”計算的,並執行您希望它執行的操作:當第一個列表按字典順序排在第二個列表之前時,它將准確地評估為True
。 當你向求解器斷言這是一個事實時,它會強制對模型施加約束。
希望這能讓你繼續前進。 您需要考慮“構建”符號表達式,因此 Python 的內部if-then-else
不是您可以使用的,因為它不能正確處理符號值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.