简体   繁体   English

在 Z3 或 Z3py 中部分描述 function

[英]Describing a function partially in Z3 or Z3py

This is my first question in Stackoverflow.这是我在 Stackoverflow 上的第一个问题。 So please do offer advice if I'm breaking any house rules.因此,如果我违反任何家规,请务必提供建议。

I have started learning Z3Py only recently.我最近才开始学习 Z3Py。 I found myself in a situation where I know the following about an Int->Int function A that I want to describe and leave it for Z3Py to solve:我发现自己处于一种情况,我知道以下关于我想描述的 Int->Int function A并将其留给 Z3Py 解决的情况:

The summation of A(x) = 10000 where 0<x<100
A(x) > 10 where 0<x<100
A(x) = 0 where x>= 100
A(3) = 190
A(12) = 1200

How can these constraints be described in Z3Py (Or Z3)?如何在 Z3Py(或 Z3)中描述这些约束?

There could be multiple ways to code this example in z3py.在 z3py 中可以有多种方法来编写这个示例。 With the constraints as you listed, you can use a symbolic array, an uninterpreted function or use separate variables for each of the 100 elements.使用您列出的约束,您可以使用符号数组、未解释的 function 或为 100 个元素中的每一个使用单独的变量。

Using arrays使用 arrays

The simplest for this case would be the use of arrays: That is, model the Int -> Int function as an array:对于这种情况,最简单的方法是使用 arrays:即 model Int -> Int function 作为数组:

from z3 import *

A = Array('A', IntSort(), IntSort())

s = Solver()

total = 0
for i in range(100):
    s.add(A[i] > 10)
    total = total + A[i]

s.add(total == 10000)

s.add(A[ 3] ==  190)
s.add(A[12] == 1200)

k = Int('k')
s.add(ForAll(k, (Implies(Or(k < 0, k >= 100),  A[k] == 0))))

if s.check() == sat:
    m = s.model()
    for i in range(100):
        v = m.evaluate(A[i]).as_long()
        print("%3d: %d" % (i, v))

When run, this prints:运行时,将打印:

  0: 131
  1: 19
  2: 436
  3: 190
  4: 12
  5: 11
  6: 19
  7: 24
  8: 11
  9: 13
 10: 133
 11: 134
 12: 1200
 13: 39
 14: 132
 15: 134
 16: 134
 17: 134
 18: 30
 19: 132
 20: 132
 21: 38
 22: 16
 23: 132
 24: 22
 25: 132
 26: 134
 27: 27
 28: 134
 29: 76
 30: 130
 31: 15
 32: 132
 33: 134
 34: 31
 35: 123
 36: 35
 37: 58
 38: 123
 39: 64
 40: 49
 41: 20
 42: 139
 43: 24
 44: 134
 45: 11
 46: 132
 47: 132
 48: 22
 49: 11
 50: 134
 51: 134
 52: 134
 53: 132
 54: 132
 55: 11
 56: 134
 57: 11
 58: 11
 59: 132
 60: 11
 61: 71
 62: 134
 63: 58
 64: 132
 65: 132
 66: 134
 67: 134
 68: 39
 69: 74
 70: 132
 71: 134
 72: 134
 73: 11
 74: 18
 75: 134
 76: 16
 77: 132
 78: 17
 79: 132
 80: 132
 81: 132
 82: 15
 83: 132
 84: 132
 85: 134
 86: 15
 87: 132
 88: 134
 89: 18
 90: 132
 91: 132
 92: 132
 93: 132
 94: 12
 95: 132
 96: 22
 97: 121
 98: 24
 99: 11

You can sum up the values printed to ensure it indeed gives you 10000 .您可以总结打印的值以确保它确实为您提供10000

Using uninterpreted functions使用未解释的函数

You can also model A as an uninterpreted function.您也可以将 model A作为未解释的 function。 The changes required is really trivial to the above:所需的更改对于上述内容来说确实微不足道:

from z3 import *

A = Function('A', IntSort(), IntSort())

s = Solver()

total = 0
for i in range(100):
    s.add(A(i) > 10)
    total = total + A(i)

s.add(total == 10000)

s.add(A( 3) ==  190)
s.add(A(12) == 1200)

k = Int('k')
s.add(ForAll(k, (Implies(Or(k < 0, k >= 100),  A(k) == 0))))

if s.check() == sat:
    m = s.model()
    for i in range(100):
        v = m.evaluate(A(i)).as_long()
        print("%3d: %d" % (i, v))

This prints:这打印:

  0: 11
  1: 11
  2: 11
  3: 190
  4: 11
  5: 11
  6: 11
  7: 11
  8: 11
  9: 11
 10: 11
 11: 11
 12: 1200
 13: 11
 14: 11
 15: 11
 16: 11
 17: 11
 18: 11
 19: 11
 20: 11
 21: 11
 22: 11
 23: 11
 24: 11
 25: 11
 26: 11
 27: 11
 28: 11
 29: 11
 30: 11
 31: 11
 32: 11
 33: 11
 34: 11
 35: 11
 36: 11
 37: 11
 38: 11
 39: 11
 40: 11
 41: 11
 42: 11
 43: 11
 44: 11
 45: 11
 46: 11
 47: 11
 48: 11
 49: 11
 50: 11
 51: 11
 52: 11
 53: 11
 54: 11
 55: 11
 56: 11
 57: 11
 58: 11
 59: 11
 60: 11
 61: 11
 62: 11
 63: 11
 64: 11
 65: 11
 66: 11
 67: 11
 68: 11
 69: 11
 70: 11
 71: 11
 72: 11
 73: 11
 74: 7543
 75: 11
 76: 11
 77: 11
 78: 11
 79: 11
 80: 11
 81: 11
 82: 11
 83: 11
 84: 11
 85: 11
 86: 11
 87: 11
 88: 11
 89: 11
 90: 11
 91: 11
 92: 11
 93: 11
 94: 11
 95: 11
 96: 11
 97: 11
 98: 11
 99: 11

The model is quite different in this case, but it's easy to see that it has A(3) = 190 , A(12) = 1200 , A(74) = 7543 , and all other entries are set to 11; model 在这种情况下完全不同,但很容易看出它有A(3) = 190A(12) = 1200A(74) = 7543 ,并且所有其他条目都设置为 11; giving you a total of 190 + 1200 + 7543 + 11 * 97 = 10000 .给你总共190 + 1200 + 7543 + 11 * 97 = 10000

Using separate variables使用单独的变量

Yet a third method would be to allocate 100 integer elements in a python array, and assert the constraints on individual elements separately.第三种方法是在 python 数组中分配 100 个 integer 元素,并分别声明对各个元素的约束。 This would lead to the simplest model, as it would not use any quantification.这将导致最简单的 model,因为它不会使用任何量化。 Of course, this would model the fact that elements outside the range 0..99 are 0 implicitly, so only use this if this constraint does not need to be explicitly mentioned.当然,这将使 model 范围0..99之外的元素隐式为 0,因此仅在不需要显式提及此约束时才使用它。 Again, the coding is almost identical:同样,编码几乎相同:

from z3 import *

A = [Int('A_%d' % i) for i in range(100)]

s = Solver()

total = 0
for i in range(100):
    s.add(A[i] > 10)
    total = total + A[i]

s.add(total == 10000)

s.add(A[ 3] ==  190)
s.add(A[12] == 1200)

if s.check() == sat:
    m = s.model()
    for i in range(100):
        v = m.evaluate(A[i]).as_long()
        print("%3d: %d" % (i, v))

I'm eliding the output of this for brevity.为简洁起见,我省略了 output 。 Note how we're using A as a python list in this case, instead of a symbolic array directly supported by z3py itself.请注意,在这种情况下,我们如何使用A作为 python 列表,而不是 z3py 本身直接支持的符号数组。

Summary概括

At the end of the day, the constraints you've described are simple enough that it can use all these three techniques.归根结底,您所描述的约束非常简单,可以使用所有这三种技术。 Which one is the best depends on what other constraints you'd want to model.哪一个是最好的取决于你想要 model 的其他约束。 (In particular, you'd want to avoid anything that makes heavy use of quantifiers, like ForAll above since SMT solvers don't do all that well for quantifiers in general. This problem is simple enough so it doesn't pose an issue, but when your constraints get complicated, it can increase solving times or lead the solver to answer unknown .) (特别是,您要避免任何大量使用量词的事情,例如上面的ForAll ,因为 SMT 求解器通常不能很好地处理量词。这个问题很简单,所以不会造成问题,但是当您的约束变得复杂时,它可能会增加求解时间或导致求解器回答unknown问题。)

Hope this gets you started.希望这能让你开始。 Best of luck!祝你好运!

Below you'll find a straight-forward SMT encoding of your problem, except for the summation requirement.除了求和要求外,您将在下面找到问题的直接 SMT 编码。

(set-option :smt.mbqi true) ; also try false (e-matching instead of MBQI)

(declare-fun A (Int) Int)

(assert 
  (=
    10000
    (+ 
      (A 1)
      (A 2)
      (A 3)
      ; ...
      (A 99))))

; A(x) > 10 where 0<x<100
(assert (forall ((x Int))
  (implies
    (and (< 0 x) (< x 100))
    (> (A x) 10))))

; A(x) = 0 where x>= 100
(assert (forall ((x Int))
  (implies
    (>= x 100)
    (> (A x) 10))))

; A(3) = 190
(assert (= (A 3) 190))

; A(12) = 1200
(assert (= (A 12) 1200))

(check-sat)

A few remarks:几点说明:

  • All SMT functions are total;所有 SMT 功能都是完整的; if you want to encode a partial function, you'll have to explicitly model the function's domain, and make definitional axioms conditional on whether or not an argument is in the domain.如果要对部分 function 进行编码,则必须明确 model 函数的域,并根据参数是否在域中来定义定义公理。 Z3 will still give you a total model for the function, though.不过,Z3 仍会为您提供 function 的总 model。

  • Arbitrary summation is non-trivial to encode in SMT, since there is no sum comprehension operator.任意求和在 SMT 中进行编码并非易事,因为没有求和运算符。 Your case is simpler, though, since your sum ranges over a statically known number of elements ( 0 < x < 100 ).但是,您的情况更简单,因为您的总和范围是静态已知数量的元素( 0 < x < 100 )。

  • I'd not be surprised if Z3py offers a convenient source syntax to generate sums over statically-known many elements如果 Z3py 提供了一种方便的源语法来生成静态已知的许多元素的总和,我不会感到惊讶

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM