Z3 的 C API 中 `Z3_mk_forall` 和 `Z3_mk_forall_const` 之间的区别?
Difference between `Z3_mk_forall` and `Z3_mk_forall_const` in C API for Z3?
我对这两个功能感到困惑。他们似乎采用了相同的参数集(一个可以直接转换为另一个)并且每个 returns 一个 AST。这些功能做同样的事情吗?如果没有,我什么时候需要每个?
2人签名:
Z3_ast Z3_mk_forall (Z3_context c,
unsigned weight,
unsigned num_patterns,
Z3_pattern const patterns[],
unsigned num_decls,
Z3_sort const sorts[],
Z3_symbol const decl_names[],
Z3_ast body)
Z3_ast Z3_mk_forall_const (Z3_context c,
unsigned weight,
unsigned num_bound,
Z3_app const bound[],
unsigned num_patterns,
Z3_pattern const patterns[],
Z3_ast body)
是的,Z3 团队提供了多种方法来完成同一件事。主要区别在于 Z3_mk_forall_const
采用使用正常机制定义的常量列表,而 Z3_mk_forall
需要使用 Z3_mk_bound
.
创建的绑定变量列表
哪种机制更容易使用取决于您的具体应用。特别是,在我看来,当您要在其上构建量词的符号数量较少且固定时, Z3_mk_forall_const
会更自然。相反,Z3_mk_forall
在量词中的符号数量可能不同的情况下可能更自然,在这种情况下,生成一个绑定变量数组是很自然的,您将使用索引对其进行寻址。
还有其他优点和缺点。例如,看到这个问题:
"How to declare constants to use as bound variables in Z3_mk_forall_const?"
在那个问题中,提问者希望避免在他们的全局上下文中引入很多变量,这将是使用 Z3_mk_forall_const
所必需的。回答者 (Christoph) 建议改用 Z3_mk_forall
,但这也不理想,因为对于嵌套的量词,这将导致每个量词的索引不同。 Christoph 还在那个答案中透露,在内部,基于 Z3_mk_forall_const
的方法被翻译成等同于 Z3_mk_forall
的方法,所以在底层实际上没有区别。然而,API 差异对程序员来说可能会有很大的不同。
C++ API 中还为程序员提供了一种(更简单的)机制,如果您能够使用它的话。以下是使用三种不同方法的示例:
// g++ --std=c++11 z3-quantifier-support.cpp -I../src/api/ -I../src/api/c++/ libz3.so
#include <stdio.h>
#include "z3.h"
#include <iostream>
#include "z3++.h"
using namespace z3;
/**
* This is by far the most concise and easiest to use if the C++ API is available to you.
*/
void example_cpp_forall() {
context c;
expr a = c.int_const("a");
expr b = c.int_const("b");
expr x = c.int_const("x");
expr axiom = forall(x, implies(x <= a, x < b));
std::cout << "Result obtained using the C++ API with forall:\n" << axiom << "\n\n";
}
/**
* Example using Z3_mk_forall_const. Not as clean as the C++ example, but this was still
* significantly easier for me to get working than the example using Z3_mk_forall().
*/
void example_c_Z3_mk_forall_const() {
// Get the context
Z3_config cfg;
Z3_context ctx;
cfg = Z3_mk_config();
ctx = Z3_mk_context(cfg);
// Declare integers a, b, and x
Z3_sort I = Z3_mk_int_sort(ctx);
Z3_symbol a_S = Z3_mk_string_symbol(ctx, "a");
Z3_symbol b_S = Z3_mk_string_symbol(ctx, "b");
Z3_symbol x_S = Z3_mk_string_symbol(ctx, "x");
Z3_ast a_A = Z3_mk_const(ctx, a_S, I);
Z3_ast b_A = Z3_mk_const(ctx, b_S, I);
Z3_ast x_A = Z3_mk_const(ctx, x_S, I);
// Build the AST (x <= a) --> (x < b)
Z3_ast x_le_a = Z3_mk_le(ctx, x_A, a_A);
Z3_ast x_lt_b = Z3_mk_lt(ctx, x_A, b_A);
Z3_ast f = Z3_mk_implies(ctx, x_le_a, x_lt_b);
Z3_app vars[] = {(Z3_app) x_A};
Z3_ast axiom = Z3_mk_forall_const(ctx, 0, 1, vars, 0, 0, f);
printf("Result obtained using the C API with Z3_mk_forall_const:\n");
printf("%s\n\n", Z3_ast_to_string(ctx, axiom));
}
/**
* Example using Z3_mk_forall. For the example, this is the most cumbersome.
*/
void example_c_Z3_mk_forall() {
// Get the context
Z3_config cfg;
Z3_context ctx;
cfg = Z3_mk_config();
ctx = Z3_mk_context(cfg);
// Declare integers a and b
Z3_sort I = Z3_mk_int_sort(ctx);
Z3_symbol a_S = Z3_mk_string_symbol(ctx, "a");
Z3_symbol b_S = Z3_mk_string_symbol(ctx, "b");
Z3_ast a_A = Z3_mk_const(ctx, a_S, I);
Z3_ast b_A = Z3_mk_const(ctx, b_S, I);
// Declare bound variables, in this case, just x
Z3_symbol x_S = Z3_mk_string_symbol(ctx, "x");
Z3_ast x_A = Z3_mk_bound(ctx, 0, I);
// Z3_mk_forall requires all names, types, and bound variables to be provided in
// arrays. In this example, where there is only one quantified variable, this seems a
// bit cumbersome. If we were dealing with an varying number of quantified variables,
// then this would seem more reasonable.
const unsigned sz = 1;
const Z3_sort types[] = {I};
const Z3_symbol names[] = {x_S};
const Z3_ast xs[] = {x_A};
// Build the AST (x <= a) --> (x < b)
Z3_ast x_le_a = Z3_mk_le(ctx, x_A, a_A);
Z3_ast x_lt_b = Z3_mk_lt(ctx, x_A, b_A);
Z3_ast f = Z3_mk_implies(ctx, x_le_a, x_lt_b);
// In the Z3 docs for Z3_mk_pattern, the following sentence appears: "If a pattern is
// not provided for a quantifier, then Z3 will automatically compute a set of
// patterns for it." So I tried supplying '0' for the number of patterns, and 'NULL'
// for the list of patterns, and Z3_mk_forall still seems to function.
Z3_ast axiom = Z3_mk_forall(ctx, 0, 0, NULL, sz, types, names, f);
printf("Result obtained using the C API with Z3_mk_forall:\n");
printf("%s\n", Z3_ast_to_string(ctx, axiom));
}
int main() {
example_cpp_forall();
example_c_Z3_mk_forall_const();
example_c_Z3_mk_forall();
}
我还发现这些问题很有帮助:
Z3 源代码中提供的示例和注释也很有帮助,特别是在 examples/c/test_capi.c
, examples/c++/example.cpp
, and src/api/z3_api.h
。
我对这两个功能感到困惑。他们似乎采用了相同的参数集(一个可以直接转换为另一个)并且每个 returns 一个 AST。这些功能做同样的事情吗?如果没有,我什么时候需要每个?
2人签名:
Z3_ast Z3_mk_forall (Z3_context c,
unsigned weight,
unsigned num_patterns,
Z3_pattern const patterns[],
unsigned num_decls,
Z3_sort const sorts[],
Z3_symbol const decl_names[],
Z3_ast body)
Z3_ast Z3_mk_forall_const (Z3_context c,
unsigned weight,
unsigned num_bound,
Z3_app const bound[],
unsigned num_patterns,
Z3_pattern const patterns[],
Z3_ast body)
是的,Z3 团队提供了多种方法来完成同一件事。主要区别在于 Z3_mk_forall_const
采用使用正常机制定义的常量列表,而 Z3_mk_forall
需要使用 Z3_mk_bound
.
哪种机制更容易使用取决于您的具体应用。特别是,在我看来,当您要在其上构建量词的符号数量较少且固定时, Z3_mk_forall_const
会更自然。相反,Z3_mk_forall
在量词中的符号数量可能不同的情况下可能更自然,在这种情况下,生成一个绑定变量数组是很自然的,您将使用索引对其进行寻址。
还有其他优点和缺点。例如,看到这个问题:
"How to declare constants to use as bound variables in Z3_mk_forall_const?"
在那个问题中,提问者希望避免在他们的全局上下文中引入很多变量,这将是使用 Z3_mk_forall_const
所必需的。回答者 (Christoph) 建议改用 Z3_mk_forall
,但这也不理想,因为对于嵌套的量词,这将导致每个量词的索引不同。 Christoph 还在那个答案中透露,在内部,基于 Z3_mk_forall_const
的方法被翻译成等同于 Z3_mk_forall
的方法,所以在底层实际上没有区别。然而,API 差异对程序员来说可能会有很大的不同。
C++ API 中还为程序员提供了一种(更简单的)机制,如果您能够使用它的话。以下是使用三种不同方法的示例:
// g++ --std=c++11 z3-quantifier-support.cpp -I../src/api/ -I../src/api/c++/ libz3.so
#include <stdio.h>
#include "z3.h"
#include <iostream>
#include "z3++.h"
using namespace z3;
/**
* This is by far the most concise and easiest to use if the C++ API is available to you.
*/
void example_cpp_forall() {
context c;
expr a = c.int_const("a");
expr b = c.int_const("b");
expr x = c.int_const("x");
expr axiom = forall(x, implies(x <= a, x < b));
std::cout << "Result obtained using the C++ API with forall:\n" << axiom << "\n\n";
}
/**
* Example using Z3_mk_forall_const. Not as clean as the C++ example, but this was still
* significantly easier for me to get working than the example using Z3_mk_forall().
*/
void example_c_Z3_mk_forall_const() {
// Get the context
Z3_config cfg;
Z3_context ctx;
cfg = Z3_mk_config();
ctx = Z3_mk_context(cfg);
// Declare integers a, b, and x
Z3_sort I = Z3_mk_int_sort(ctx);
Z3_symbol a_S = Z3_mk_string_symbol(ctx, "a");
Z3_symbol b_S = Z3_mk_string_symbol(ctx, "b");
Z3_symbol x_S = Z3_mk_string_symbol(ctx, "x");
Z3_ast a_A = Z3_mk_const(ctx, a_S, I);
Z3_ast b_A = Z3_mk_const(ctx, b_S, I);
Z3_ast x_A = Z3_mk_const(ctx, x_S, I);
// Build the AST (x <= a) --> (x < b)
Z3_ast x_le_a = Z3_mk_le(ctx, x_A, a_A);
Z3_ast x_lt_b = Z3_mk_lt(ctx, x_A, b_A);
Z3_ast f = Z3_mk_implies(ctx, x_le_a, x_lt_b);
Z3_app vars[] = {(Z3_app) x_A};
Z3_ast axiom = Z3_mk_forall_const(ctx, 0, 1, vars, 0, 0, f);
printf("Result obtained using the C API with Z3_mk_forall_const:\n");
printf("%s\n\n", Z3_ast_to_string(ctx, axiom));
}
/**
* Example using Z3_mk_forall. For the example, this is the most cumbersome.
*/
void example_c_Z3_mk_forall() {
// Get the context
Z3_config cfg;
Z3_context ctx;
cfg = Z3_mk_config();
ctx = Z3_mk_context(cfg);
// Declare integers a and b
Z3_sort I = Z3_mk_int_sort(ctx);
Z3_symbol a_S = Z3_mk_string_symbol(ctx, "a");
Z3_symbol b_S = Z3_mk_string_symbol(ctx, "b");
Z3_ast a_A = Z3_mk_const(ctx, a_S, I);
Z3_ast b_A = Z3_mk_const(ctx, b_S, I);
// Declare bound variables, in this case, just x
Z3_symbol x_S = Z3_mk_string_symbol(ctx, "x");
Z3_ast x_A = Z3_mk_bound(ctx, 0, I);
// Z3_mk_forall requires all names, types, and bound variables to be provided in
// arrays. In this example, where there is only one quantified variable, this seems a
// bit cumbersome. If we were dealing with an varying number of quantified variables,
// then this would seem more reasonable.
const unsigned sz = 1;
const Z3_sort types[] = {I};
const Z3_symbol names[] = {x_S};
const Z3_ast xs[] = {x_A};
// Build the AST (x <= a) --> (x < b)
Z3_ast x_le_a = Z3_mk_le(ctx, x_A, a_A);
Z3_ast x_lt_b = Z3_mk_lt(ctx, x_A, b_A);
Z3_ast f = Z3_mk_implies(ctx, x_le_a, x_lt_b);
// In the Z3 docs for Z3_mk_pattern, the following sentence appears: "If a pattern is
// not provided for a quantifier, then Z3 will automatically compute a set of
// patterns for it." So I tried supplying '0' for the number of patterns, and 'NULL'
// for the list of patterns, and Z3_mk_forall still seems to function.
Z3_ast axiom = Z3_mk_forall(ctx, 0, 0, NULL, sz, types, names, f);
printf("Result obtained using the C API with Z3_mk_forall:\n");
printf("%s\n", Z3_ast_to_string(ctx, axiom));
}
int main() {
example_cpp_forall();
example_c_Z3_mk_forall_const();
example_c_Z3_mk_forall();
}
我还发现这些问题很有帮助:
Z3 源代码中提供的示例和注释也很有帮助,特别是在 examples/c/test_capi.c
, examples/c++/example.cpp
, and src/api/z3_api.h
。