使用Z3 C ++ API进行浮点运算

时间:2018-05-29 03:36:53

标签: c++ floating-point z3

我试图用Z3解决非线性实数问题。我需要Z3来生成多个解决方案。 在问题领域,精确度不是一个关键问题;小数点后我只需要一个或两个十进制数字。所以,我需要设置Z3不要探索实数的所有搜索空间,以尽量减少找到多个解决方案的时间。

我试图用浮点数替换实数。我在c_api.c文件中读到了fpa示例,但我发现它对我来说有点混乱。

例如,我假设我想在以下代码中转换实数:

config cfg;
cfg.set("auto_config", true);
context con(cfg);

expr x = con.real_const("x");
expr y = con.real_const("y");

solver sol(con);
sol.add(x*y > 10);
std::cout << sol.check() << "\n";
std::cout << sol.get_model() << "\n";

}

我尝试了以下代码,但它无效

config cfg;
cfg.set("auto_config", true);
context con(cfg);

expr sign = con.bv_const("sig", 1);
expr exp = con.bv_const("exp", 10);
expr sig = con.bv_const("sig", 10);


expr x = to_expr(con, Z3_mk_fpa_fp(con, sign, exp, sig));
expr y = to_expr(con, Z3_mk_fpa_fp(con, sign, exp, sig));

solver sol(con);
sol.add(x*y > 10);

std::cout << sol.check() << "\n";

,输出为:

Assertion failed: false, file c:\users\rehab\downloads\z3-master\z3-master\src\a
pi\c++\z3++.h, line 1199

我的问题是:

  1. 是否有关于在C ++ API中使用fpa的详细示例或代码片段?我不清楚如何将C API中的fpa示例转换为C ++ API。
  2. 上述代码转换有什么问题?

1 个答案:

答案 0 :(得分:1)

我不确定使用花车是否是解决问题的最佳方法。但听起来你尝试了所有其他选项,并且非线性正在妨碍你。请注意,即使使用浮点数模拟问题,浮点算法也非常棘手,求解器可能很难找到令人满意的模型。此外,由于数值不稳定,解决方案可能远离实际结果。

使用C

将所有这些放在一边,使用C api编写查询的正确方法是(假设我们使用32位单精度浮点数):

#include <z3.h>

int main(void) {
    Z3_config  cfg = Z3_mk_config();
    Z3_context ctx = Z3_mk_context(cfg);
    Z3_solver  s   = Z3_mk_solver(ctx);

    Z3_solver_inc_ref(ctx, s);
    Z3_del_config(cfg);

    Z3_sort float_sort = Z3_mk_fpa_sort(ctx, 8, 24);

    Z3_symbol s_x = Z3_mk_string_symbol(ctx, "x");
    Z3_symbol s_y = Z3_mk_string_symbol(ctx, "y");
    Z3_ast      x = Z3_mk_const(ctx, s_x, float_sort);
    Z3_ast      y = Z3_mk_const(ctx, s_y, float_sort);

    Z3_symbol s_x_times_y = Z3_mk_string_symbol(ctx, "x_times_y");
    Z3_ast      x_times_y = Z3_mk_const(ctx, s_x_times_y, float_sort);

    Z3_ast c1 = Z3_mk_eq(ctx, x_times_y, Z3_mk_fpa_mul(ctx, Z3_mk_fpa_rne(ctx), x, y));

    Z3_ast c2 = Z3_mk_fpa_gt(ctx, x_times_y, Z3_mk_fpa_numeral_float(ctx, 10, float_sort));

    Z3_solver_assert(ctx, s, c1);
    Z3_solver_assert(ctx, s, c2);

    Z3_lbool result = Z3_solver_check(ctx, s);

    switch(result) {
        case Z3_L_FALSE: printf("unsat\n");
                         break;
        case Z3_L_UNDEF: printf("undef\n");
                         break;
        case Z3_L_TRUE:  { Z3_model m = Z3_solver_get_model(ctx, s);
                           if(m) Z3_model_inc_ref(ctx, m);
                           printf("sat\n%s\n", Z3_model_to_string(ctx, m));
                           break;
                         }
    }

    return 0;
}

运行时,会打印:

sat
x_times_y -> (fp #b0 #xbe #b10110110110101010000010)
y -> (fp #b0 #xb5 #b00000000000000000000000)
x -> (fp #b0 #x88 #b10110110110101010000010)

这些是单精度浮点数;你可以在维基百科中阅读它们。在更传统的表示法中,它们是:

x_times_y -> 1.5810592e19
y         -> 1.8014399e16
x         -> 877.6642

使用起来非常棘手,但问的是什么。

使用Python

我衷心建议使用Python API,在投资这样复杂的C代码之前,至少要了解解算器的功能。以下是Python的外观:

from z3 import *

x = FP('x', FPSort(8, 24))
y = FP('y', FPSort(8, 24))

s = Solver()
s.add(x*y > 10);
s.check()
print s.model()

运行时,会打印:

[y = 1.32167303562164306640625,
 x = 1.513233661651611328125*(2**121)]

也许不是你所期望的,但它确实是一个有效的模型。

使用Haskell

为了让您体验简洁,这里是如何使用Haskell绑定表达相同的问题(它只是一个衬垫!)

Prelude Data.SBV> sat $ \x y -> fpIsPoint x &&& fpIsPoint y &&& x * y .> (10::SFloat)
Satisfiable. Model:
  s0 = 5.1129496e28 :: Float
  s1 =  6.6554557e9 :: Float

摘要

请注意,浮点数也存在NaN / Infinity值的问题,因此您可能必须明确避免这些问题。 (这就是Haskell表达式使用isFPPoint谓词所做的。用Python或C编写代码需要更多的代码,但肯定是可行的。)

应该强调的是,字面上任何其他绑定到Z3(Python,Haskell,Scala,你有什么)都会给你一个比你用C / C ++ / Java更好的体验。 (即使在SMTLib中直接编码也会更好。)

所以,我衷心建议使用一些更高级别的界面(Python是一个很好的界面:它很容易学习),一旦你对模型及其工作原理有信心,你就可以开始在C中编写相同的代码了。如果有必要的话。