Z3 имеет предикат Distinct
, который гарантирует, что все элементы различны, но, насколько мне известно, нет встроенного distinct-except
.
Для кодирования такого рода ограничения, Я бы использовал массив для отслеживания мощности вставленных элементов. Примерно так:
from z3 import *
def distinct_except(G, ignored):
if len(G) < 2:
return BoolSort().cast(True)
A = K(G[0].sort(), 0);
for i in range(len(G)):
A = Store(A, G[i], If(G[i] == ignored, 0, 1 + Select(A, G[i])))
res = True
for i in range(len(G)):
res = And(res, Select(A, G[i]) <= 1)
return res
Мы просто вставляем элементы в массив, увеличивая счет на 1, если элемент не игнорируется, в противном случае добавляем 0. Это позволяет избежать дорогостоящих узлов if-then-else как массив построен линейно. Затем мы снова пройдемся по списку и убедимся, что никогда не сохранили ничего больше 1.
. Размеры выражений res
и A
останутся линейными, и z3 сможет справиться с этим. довольно эффективно. Я хотел бы услышать, если вы найдете иначе.
Вот несколько тестов, чтобы увидеть его в действии:
# Test: Create four variables, assert they are distinct in the above sense
a, b, c, d = BitVecs('a b c d', 32)
s = Solver()
s.add(distinct_except([a, b, c, d], 0))
# Clearly sat:
print s.check()
print s.model()
# Add a constraint that a and b are equal
# Still SAT, because we can make a and b 0
s.add(a == b)
print s.check()
print s.model()
# Force a to be non-zero
s.add(a != 0)
# Now we have unsat:
print s.check()
Это печатает:
sat
[b = 1024, a = 16, c = 1, d = 536870912]
sat
[c = 33554432, a = 0, d = 32768, b = 0]
unsat
Обратите внимание, что вы всегда можете использовать print s.sexpr()
, чтобы увидеть выражения, которые вы строите, прежде чем вызывать s.check()
, чтобы увидеть, как они растут по мере увеличения ваших входных списков.