Z3 Java 为什么我需要一个战术?

Z3 Java why do I need a tactic for this?

我有以下 Java 程序可以解决 (not (and (= b false) (= d false)))。它不适用于开箱即用的 context/solver。当我尝试 model.getConstInterp(d).

时,看起来 D 常量从模型中删除了

如果我 运行 这个和 andThen(mkTactic("simplify"), mkTactic("sat")) 它工作正常。如果我尝试 运行 在 z3 二进制文件的 SMT 脚本中使用它,它无需任何特殊策略就可以正常工作。

我错过了什么?我认为这是一个上下文选项。

是的,我意识到谓词 (not (and (= b false) (= d false))) 可以写得更简单,但这是由一些其他代码自动生成的,不太容易追踪和更改。该语句实际上是一个更大的生成程序的一部分。

  @Test
  void testQuestion() {
    var ctx = new Context();
    var b = ctx.mkBoolConst("B");
    var d = ctx.mkBoolConst("D");
    var solver = ctx.mkSolver();

    var p4 = ctx.mkNot(
        ctx.mkAnd(
            ctx.mkEq(b, ctx.mkFalse()),
            ctx.mkEq(d, ctx.mkFalse())));
    solver.assertAndTrack(p4, ctx.mkBoolConst("P4"));

    var result = solver.check();
    assertEquals(Status.SATISFIABLE, result);
    var model = solver.getModel();
    assertEquals("true", model.getConstInterp(b).toString());

    // this throws NPE as the cgid_enable declaration is missing
    assertEquals("false", model.getConstInterp(d).toString());
  }

默认情况下,z3 只会在需要 时才为模型中的变量赋值。不幸的是,“需要”的意思是 z3;如果底层引擎已经显示 sat 没有分配给某些变量的问题,那么它们将保持未分配状态。哪些变量将未分配可以从 run-to-run/version-to-version.

更改

在您的情况下,一旦求解器将 b 指定为 true,它就会注意到它的工作已完成;因为分配给 d 不再重要。 (当然,通过对称性,它也可以反过来做。)因此,它未分配,稍后会导致您的 NPE。

解决方案是始终要求完成,使用 eval 函数。这是您的代码,未使用测试框架编码,而是使用 eval 来说明问题:

import com.microsoft.z3.*;

class JavaZ3Example {
   public static void main(String [] args) {
         var ctx = new Context();
         var b = ctx.mkBoolConst("B");
         var d = ctx.mkBoolConst("D");
         var solver = ctx.mkSolver();

         var p4 = ctx.mkNot(
             ctx.mkAnd(
                 ctx.mkEq(b, ctx.mkFalse()),
                 ctx.mkEq(d, ctx.mkFalse())));
         solver.assertAndTrack(p4, ctx.mkBoolConst("P4"));

         var result = solver.check();
         System.out.println(result);
         var model = solver.getModel();
         System.out.println(model.eval(b, false).toString());
         System.out.println(model.eval(d, false).toString());
   };
};

如果你运行以上,你会得到:

SATISFIABLE
true
D

这就是最后一行中的告密标志,求解器决定 为变量 D 赋值。也就是说,它没有分配;在此上下文中,它仅打印为变量的名称。

您只需将 true 传递给 eval 的第二个参数即可更改此行为。传递 true 作为第二个参数告诉 z3 确保所有变量都被赋值,无论是否需要赋值。即最后两行改为:

         System.out.println(model.eval(b, true).toString());
         System.out.println(model.eval(d, true).toString());

当您 运行 新程序时,您将获得:

SATISFIABLE
true
false

当然,最后一行也可以显示为 true,但是当您强制 eval 为每个变量赋值时,该信息将丢失。

长话短说,对于这类需要每个模型值都赋值的问题,不要使用getConstInterp。相反,使用 eval,为第二个参数传递 true