Z3 求解器 Java API:意外行为
Z3 Solver Java API: Unexpected behaviour
通过向求解器添加条件,我想用 "solver.check()" 检查是否存在解。因此,我创建了一个简单示例来找到 t1 的解决方案。我知道 t1 有一个解,即 t1 = 0。然而,求解器没有状态 "SATISFIABLE"。
public static void main(String[] args) {
int h_max = 7;
HashMap<String, String> cfg = new HashMap<String, String>();
cfg.put("model", "true");
Context ctx = new Context(cfg);
FPSort s = ctx.mkFPSort(5, 20);
Solver solver = ctx.mkSolver();
Model model = null;
// Initialize constants
RealExpr half = ctx.mkFPToReal(ctx.mkFP(0.5, s));
RealExpr g = ctx.mkFPToReal(ctx.mkFP(9.81, s));
RealExpr hmax = ctx.mkInt2Real(ctx.mkInt(h_max));
RealExpr v = ctx.mkFPToReal(ctx.mkFP((Math.sqrt(2*h_max*9.81)), s));
// Create query Information
RealExpr q2 = ctx.mkReal(1);
RealExpr q2Min = (RealExpr) ctx.mkSub(q2, half);
RealExpr q2Max = (RealExpr) ctx.mkAdd(q2, half);
// Initialize constraints
RealExpr tmax = ctx.mkFPToReal(ctx.mkFP((Math.sqrt(2*h_max/9.81)), s));
RealExpr t0 = ctx.mkReal(0);
// Initialize sampling interval
RealExpr ts = ctx.mkFPToReal(ctx.mkFP(Math.sqrt(2*h_max/9.81)+0.1, s));
// Variable t1
RealExpr t1 = ctx.mkRealConst("t1");
// 0 <= t1 <= tmax
BoolExpr c1 = ctx.mkGe(t1, t0);
BoolExpr c2 = ctx.mkLe(t1,tmax);
// Elapsed Times
RealExpr tE = (RealExpr) ctx.mkAdd(ts, t1);
// Add conditions to solver
solver.add(c1);
solver.add(c2);
// Calculating tE2 % tmax, since tE2 > tmax
RealExpr quotient = (RealExpr) ctx.mkDiv(tE, tmax);
IntExpr factor = ctx.mkReal2Int(quotient);
RealExpr t2 = (RealExpr) ctx.mkSub(tE, ctx.mkMul(factor, tmax));
// Calculating the observation h2 with t2.
RealExpr h2 = (RealExpr) ctx.mkSub(ctx.mkMul(v,t2), ctx.mkMul(half, t2, t2, g));
// Defining constraint q2Min <= h2 < q2Max
BoolExpr c3 = ctx.mkAnd(ctx.mkGe(h2, q2Min),ctx.mkLt(h2, q2Max));
solver.add(c3);
//System.out.println("solver c1: " + solver.check(c1));
//System.out.println("solver c2: " + solver.check(c2));
//System.out.println("solver c3: " + solver.check(c3));
if (solver.check() == Status.SATISFIABLE) {
model = solver.getModel();
System.out.println("System is Satisfiable");
}
else {
System.out.println("Unsatisfiable");
}
ctx.close();
}
我发现了一些意想不到的行为。
如果我在 "solver.check()" 之前尝试检查条件,例如
System.out.println("solver c2: " + solver.check(c2));
System.out.println("solver c3: " + solver.check(c3));
它输出:
solver c2: UNKNOWN
solver c3: UNKNOWN
突然间,求解器的状态变成了"SATISFIABLE"。但是如果我事先只检查一个条件,状态仍然是 "UNSATISFIABLE".
除此之外,如果我从
t1 = ctx.mkRealConst("t1");
到
t1 = ctx.mkReal(0);
求解器也找到了解,求解器状态为"SATISFIABLE"。
为什么求解器有这种行为,我怎样才能让求解器找到解决方案?我可以尝试其他方法吗?
一般来说,当你写:
solver.check(c1)
你 不是 要求 z3 检查 c1
是否可满足。您要求 z3 做的是检查您输入的所有断言是否可满足,假设 c1
为真。这称为 "check under assumptions" 并在此处记录:https://z3prover.github.io/api/html/classcom_1_1microsoft_1_1z3_1_1_solver.html#a71882930969215081ef020ec8fec45f3
起初这可能相当混乱,但它允许检查假设下的可满足性,而不必全局断言这些假设。
关于你得到 UNKNOWN
的原因。您正在使用浮点运算,并将其与实数混合和匹配。这会产生很多非线性约束,而 z3 并不能很好地处理这些问题。尽量保持逻辑分离:如果可以,不要将实数与浮点数混合。 (如果您对如何建模有疑问,请单独提问。)
最后,写t1 = ctx.mkReal(0)
和写t1 = ctx.mkRealConst("t1")
是很不一样的。第一个更容易处理:t1
只是 0。在第二种情况下,它是一个变量。因此,前者导致 z3 的问题更容易处理也就不足为奇了。同样,没有灵丹妙药,但从不以这种方式混合逻辑开始:如果你想在浮点上工作,把所有东西都放在那个地方。如果您想使用真正的价值,请保持一切真正的价值。这样你会获得更多的里程。如果您必须混合使用两者,那么您很可能会看到 UNKNOWN
结果。
通过向求解器添加条件,我想用 "solver.check()" 检查是否存在解。因此,我创建了一个简单示例来找到 t1 的解决方案。我知道 t1 有一个解,即 t1 = 0。然而,求解器没有状态 "SATISFIABLE"。
public static void main(String[] args) {
int h_max = 7;
HashMap<String, String> cfg = new HashMap<String, String>();
cfg.put("model", "true");
Context ctx = new Context(cfg);
FPSort s = ctx.mkFPSort(5, 20);
Solver solver = ctx.mkSolver();
Model model = null;
// Initialize constants
RealExpr half = ctx.mkFPToReal(ctx.mkFP(0.5, s));
RealExpr g = ctx.mkFPToReal(ctx.mkFP(9.81, s));
RealExpr hmax = ctx.mkInt2Real(ctx.mkInt(h_max));
RealExpr v = ctx.mkFPToReal(ctx.mkFP((Math.sqrt(2*h_max*9.81)), s));
// Create query Information
RealExpr q2 = ctx.mkReal(1);
RealExpr q2Min = (RealExpr) ctx.mkSub(q2, half);
RealExpr q2Max = (RealExpr) ctx.mkAdd(q2, half);
// Initialize constraints
RealExpr tmax = ctx.mkFPToReal(ctx.mkFP((Math.sqrt(2*h_max/9.81)), s));
RealExpr t0 = ctx.mkReal(0);
// Initialize sampling interval
RealExpr ts = ctx.mkFPToReal(ctx.mkFP(Math.sqrt(2*h_max/9.81)+0.1, s));
// Variable t1
RealExpr t1 = ctx.mkRealConst("t1");
// 0 <= t1 <= tmax
BoolExpr c1 = ctx.mkGe(t1, t0);
BoolExpr c2 = ctx.mkLe(t1,tmax);
// Elapsed Times
RealExpr tE = (RealExpr) ctx.mkAdd(ts, t1);
// Add conditions to solver
solver.add(c1);
solver.add(c2);
// Calculating tE2 % tmax, since tE2 > tmax
RealExpr quotient = (RealExpr) ctx.mkDiv(tE, tmax);
IntExpr factor = ctx.mkReal2Int(quotient);
RealExpr t2 = (RealExpr) ctx.mkSub(tE, ctx.mkMul(factor, tmax));
// Calculating the observation h2 with t2.
RealExpr h2 = (RealExpr) ctx.mkSub(ctx.mkMul(v,t2), ctx.mkMul(half, t2, t2, g));
// Defining constraint q2Min <= h2 < q2Max
BoolExpr c3 = ctx.mkAnd(ctx.mkGe(h2, q2Min),ctx.mkLt(h2, q2Max));
solver.add(c3);
//System.out.println("solver c1: " + solver.check(c1));
//System.out.println("solver c2: " + solver.check(c2));
//System.out.println("solver c3: " + solver.check(c3));
if (solver.check() == Status.SATISFIABLE) {
model = solver.getModel();
System.out.println("System is Satisfiable");
}
else {
System.out.println("Unsatisfiable");
}
ctx.close();
}
我发现了一些意想不到的行为。 如果我在 "solver.check()" 之前尝试检查条件,例如
System.out.println("solver c2: " + solver.check(c2));
System.out.println("solver c3: " + solver.check(c3));
它输出:
solver c2: UNKNOWN
solver c3: UNKNOWN
突然间,求解器的状态变成了"SATISFIABLE"。但是如果我事先只检查一个条件,状态仍然是 "UNSATISFIABLE".
除此之外,如果我从
t1 = ctx.mkRealConst("t1");
到
t1 = ctx.mkReal(0);
求解器也找到了解,求解器状态为"SATISFIABLE"。
为什么求解器有这种行为,我怎样才能让求解器找到解决方案?我可以尝试其他方法吗?
一般来说,当你写:
solver.check(c1)
你 不是 要求 z3 检查 c1
是否可满足。您要求 z3 做的是检查您输入的所有断言是否可满足,假设 c1
为真。这称为 "check under assumptions" 并在此处记录:https://z3prover.github.io/api/html/classcom_1_1microsoft_1_1z3_1_1_solver.html#a71882930969215081ef020ec8fec45f3
起初这可能相当混乱,但它允许检查假设下的可满足性,而不必全局断言这些假设。
关于你得到 UNKNOWN
的原因。您正在使用浮点运算,并将其与实数混合和匹配。这会产生很多非线性约束,而 z3 并不能很好地处理这些问题。尽量保持逻辑分离:如果可以,不要将实数与浮点数混合。 (如果您对如何建模有疑问,请单独提问。)
最后,写t1 = ctx.mkReal(0)
和写t1 = ctx.mkRealConst("t1")
是很不一样的。第一个更容易处理:t1
只是 0。在第二种情况下,它是一个变量。因此,前者导致 z3 的问题更容易处理也就不足为奇了。同样,没有灵丹妙药,但从不以这种方式混合逻辑开始:如果你想在浮点上工作,把所有东西都放在那个地方。如果您想使用真正的价值,请保持一切真正的价值。这样你会获得更多的里程。如果您必须混合使用两者,那么您很可能会看到 UNKNOWN
结果。