Scala (Z3 なし) を使用して、実際には制約に対する解決策が他にもあることを示します。
val tf = Seq(true, false)
val allValid =
for(a <- tf; b <- tf; c <- tf; d <- tf;
if((a && (b ^ c)) ^ d)) yield (
(if(a) "a" else "") + (if(b) "b" else "") +
(if(c) "c" else "") + (if(d) "d" else ""))
allValid.mkString("{ ", ", ", " }")
版画:
{ abcd, ab, ac, ad, bcd, bd, cd, d }
だから私が何かを見逃していない限り、問題は、なぜすべての解決策を見つけられないのですか? これがその答えです。(ネタバレ注意: 「getAllModels」は実際にはすべてのモデルを取得するわけではありません。) まず、観察したことを再現しましょう。
import z3.scala._
val ctx = new Z3Context("MODEL" -> true)
val a = ctx.mkFreshConst("a", ctx.mkBoolSort)
val b = ctx.mkFreshConst("b", ctx.mkBoolSort)
val c = ctx.mkFreshConst("c", ctx.mkBoolSort)
val d = ctx.mkFreshConst("d", ctx.mkBoolSort)
val cstr0 = ctx.mkXor(b, c)
val cstr1 = ctx.mkAnd(a, cstr0)
val cstr2 = ctx.mkXor(cstr1, d)
ctx.assertCnstr(cstr2)
ここで、: を実行するとctx.checkAndGetAllModels.foreach(println(_))
、次のようになります。
d!3 -> false
a!0 -> true
c!2 -> false
b!1 -> true
d!3 -> true // this model is problematic
a!0 -> false
d!3 -> false
a!0 -> true
c!2 -> true
b!1 -> false
d!3 -> true
a!0 -> true
c!2 -> false
b!1 -> false
d!3 -> true
a!0 -> true
c!2 -> true
b!1 -> true
ここでの問題は、2 番目のモデルが不完全なモデルであることです。Z3 はそれを返すことができます。これは、b
との値が何であれc
、制約が満たされるためです (b
とc
はドントケア変数です)。の現在の実装ではcheckAndGetAllModels
、繰り返しを防ぐためにモデルを否定するだけです。この場合、(not (and d (not a)))
成立するような別のモデルを要求します。これにより、この 2 つの値を持つ他のすべてのモデルが返されなくなります。ある意味では、不完全なモデルは実際には 4 つの有効で完成したモデルを表しています。
ちなみに、このfindAll
関数で ScalaZ3 の DSL を使用するとどうなるかというと、すべてのモデルが不完全な場合 (および次のモデルの計算に使用される前) にデフォルト値で完成されます。DSL のコンテキストでは、式に現れる一連の変数を知っているので、これを行うことができます。この場合、モデルをどのように完成させるべきかを推測するのは難しくなります。1 つのオプションは、使用された変数を ScalaZ3 が記憶できるようにすることです。より良い解決策は、Z3 が常に don't-care 変数の値を返すか、単にモデル内のすべてのdon't-care変数をリストするオプションを持つことです。