在 z3py 中声明外部函数

Declare External Functions in z3py

场景

在数学优化领域,需要对不等式 g_k(...) 约束进行建模。 g_k(...) 有时可以是对外部程序的函数调用,就所有意图和目的而言,它是一个黑盒子。简单地找到满意的答案对于某些工程分析可能是有益的。

例子

上述场景的示例应用程序,但也可以是整数或布尔值:

min f(x,y)
g1(x,y) <= 25
g2(x,y) >= 7.7
x,y ε Real
x >= 0
x <= 50.0
y >= 0
y <= 5.0

g1和g2是调用外部程序的Python函数。函数 return 一个实数。按照 this Z3 格式查找简单满足约束的模型将表示为:

from z3 import *
from ExternalCodes import Code1, Code2 #For example, these could be Python wrappers to C++ codes

def g_1(x, y):
    return Code1(x, y) #isinstance(Code1(x,y), float) == True

def g_2(x, y):
    return Code2(x, y) #isinstance(Code2(x,y), float) == True

s = Solver()

x, y = Reals('x y')
s.add(g_1(x, y) <= 25.0)
s.add(g_2(x, y) >= 7.7)
s.add(x <= 0)
s.add(50.0 <= x)
s.add(y <= 0)
s.add(5.0 <= y)

m = s.model()
print(m)

问题

  1. 我明白Code1和Code2编辑的return类型需要是Z3 数据类型。如何将 Python 类型转换为第 1 条中提到的 Z3 类型 comment
  2. 当可能需要约束时,如何使用 Z3 查找 sat 模型 在外部代码中评估,即声明函数?我明白 可能效率低下,我会放松启发式等,因为它是不可判定的, 但对于某些工程应用,枚举一个 sat 解决方案,如果它 存在,比从一开始就使用优化器更有优势。

相关回答

您正在寻找未解释的函数。

http://ericpony.github.io/z3py-tutorial/advanced-examples.htm

中搜索字词 "uninterpreted functions"

您的问题似乎对如何使用 SMT 求解器做出了一些假设;这并不能完全反映当前的最新技术水平。阅读有关 SMT 求解器的使用的一个很好的资源是:https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/nbjorner-smt-application-chapter.pdf 花时间仔细阅读它以了解它在实践中的应用是非常值得的。

一些一般性评论:

Z3py

Z3py 不是 用于推理 Python 程序的系统。 Z3py 是库的集合,因此您可以以更清晰、更简单的方式编写 Z3 脚本。许多语言都有许多这样的绑定:C/C++/Java/Haskell/Scala,随你便。 Z3py的优点是更容易学习和使用。但是您不应该将其视为推理 Python 本身的系统。它只是一种以轻量级形式编写 Z3 脚本的方法。

求解器逻辑

SMT 求解器主要处理各种理论的(主要是无量词的)多分类逻辑的可判定片段。您可以在以下位置找到详细信息:http://smtlib.cs.uiowa.edu/logics.shtml

SMTLib

大多数求解器接受所谓的 SMT-Lib 格式的输入,详情请见此处:http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2017-07-18.pdf

请注意,C/Python/Java 等级别的任何 "binding" 只是为了方便程序员。虽然许多求解器还提供扩展功能,但您应该想到 SMTLib 语言。在您的特定情况下,上述文档中详细描述了未解释的函数。

类型

SMTLib 理解一组类型:整数、实数、位向量(机器整数)、浮点数等。它还允许复合类型,例如代数数据类型,甚至可以递归。 (尽管求解器支持各不相同。)您必须 "map" 您的外部函数类型为这些类型:希望有足够接近的东西。如果没有,请随时提出有关您感兴趣的类型的具体问题。

"Importing" 函数

不可能将用其他语言(Python/C/C++等)编写的函数导入到 SMTLib 中并对其进行推理。没有这样做的机制,也永远不会有。这不是 SMT 求解器的目标。如果您想对用特定语言编写的程序进行推理,那么您应该寻找专门设计用于这些语言的工具。 (例如 Dafny 用于一般命令式程序,Klee/CBMC 用于 C 程序,LiquidHaskell 用于 Haskell 程序等)这些工具的功能各不相同,它们允许您指定和证明。请注意,这些工具本身可以使用下面的 SMT 求解器来完成它们的任务——它们经常这样做,而不是相反。

坚持使用 SMTLib

如果没有其他工具可用(不幸的是,大多数语言都可能是这种情况,尤其是传统语言),您基本上只能使用 SMTLib 语言提供的任何工具。在您的情况下,使用 SMTLib 对此类 "external" 函数建模的最佳方法是使用未解释的函数以及公理。通常,您需要公理来限制未解释函数本身的行为,以便为您的外部函数建模。另一方面,如果公理被量化(通常它们会被量化),求解器可能 return unknown.

总结

虽然 SMT 求解器是很棒的工具,但您应该始终记住,它们运行的​​语言是 SMTLib,而不是 Python/C 或其他任何语言。这些绑定只是访问求解器的方式,因为您可能会将其合并到更大的项目中。我希望清除预期的用例。

询问有关您已尝试过的内容和您想要实现的内容(代码示例)的具体问题是从该论坛中获取更多信息的最佳方式!