英文:
Specifying modular arithmetic conditions in Z3
问题
BitVecExpr[] b = new BitVecExpr[5];
for (int i = 0; i < 5; i++)
{
b[i] = ctx.MkBitVecConst($"b_{i}", 1);
}
BoolExpr f1 = ctx.MkOr(
ctx.MkEq(ctx.MkMod(ctx.MkAdd(b[0], ctx.MkMul(b[1], ctx.MkBV(2, 1)), ctx.MkMul(b[2], ctx.MkBV(2, 2)), ctx.MkMul(b[3], ctx.MkBV(2, 3)), ctx.MkMul(b[4], ctx.MkBV(2, 4))), ctx.MkBV(1, 4)), ctx.MkBV(0, 1)),
ctx.MkEq(ctx.MkMod(ctx.MkAdd(b[0], ctx.MkMul(b[1], ctx.MkBV(2, 1)), ctx.MkMul(b[2], ctx.MkBV(2, 2)), ctx.MkMul(b[3], ctx.MkBV(2, 3)), ctx.MkMul(b[4], ctx.MkBV(2, 4))), ctx.MkBV(2, 4)), ctx.MkBV(0, 1)),
ctx.MkEq(ctx.MkMod(ctx.MkAdd(b[0], ctx.MkMul(b[1], ctx.MkBV(2, 1)), ctx.MkMul(b[2], ctx.MkBV(2, 2)), ctx.MkMul(b[3], ctx.MkBV(2, 3)), ctx.MkMul(b[4], ctx.MkBV(2, 4))), ctx.MkBV(3, 4)), ctx.MkBV(0, 1)),
ctx.MkEq(ctx.MkMod(ctx.MkAdd(b[0], ctx.MkMul(b[1], ctx.MkBV(2, 1)), ctx.MkMul(b[2], ctx.MkBV(2, 2)), ctx.MkMul(b[3], ctx.MkBV(2, 3)), ctx.MkMul(b[4], ctx.MkBV(2, 4))), ctx.MkBV(4, 4)), ctx.MkBV(0, 1))
));
BoolExpr f2 = ctx.MkOr(
ctx.MkEq(ctx.MkMod(ctx.MkAdd(b[0], ctx.MkMul(b[1], ctx.MkBV(2, 1)), ctx.MkMul(b[2], ctx.MkBV(2, 2)), ctx.MkMul(b[3], ctx.MkBV(2, 3)), ctx.MkMul(b[4], ctx.MkBV(2, 4))), ctx.MkBV(1, 4)), ctx.MkBV(0, 1)),
ctx.MkEq(ctx.MkMod(ctx.MkAdd(b[0], ctx.MkMul(b[1], ctx.MkBV(2, 1)), ctx.MkMul(b[2], ctx.MkBV(2, 2)), ctx.MkMul(b[3], ctx.MkBV(2, 3)), ctx.MkMul(b[4], ctx.MkBV(2, 4))), ctx.MkBV(2, 4)), ctx.MkBV(0, 1)),
ctx.MkEq(ctx.MkMod(ctx.MkAdd(b[0], ctx.MkMul(b[1], ctx.MkBV(2, 1)), ctx.MkMul(b[2], ctx.MkBV(2, 2)), ctx.MkMul(b[3], ctx.MkBV(2, 3)), ctx.MkMul(b[4], ctx.MkBV(2, 4))), ctx.MkBV(3, 4)), ctx.MkBV(0, 1)),
ctx.MkEq(ctx.MkMod(ctx.MkAdd(b[0], ctx.MkMul(b[1], ctx.MkBV(2, 1)), ctx.MkMul(b[2], ctx.MkBV(2, 2)), ctx.MkMul(b[3], ctx.MkBV(2, 3)), ctx.MkMul(b[4], ctx.MkBV(2, 4))), ctx.MkBV(4, 4)), ctx.MkBV(0, 1)),
ctx.MkEq(ctx.MkMod(ctx.MkAdd(b[0], ctx.MkMul(b[1], ctx.MkBV(2, 1)), ctx.MkMul(b[2], ctx.MkBV(2, 2)), ctx.MkMul(b[3], ctx.MkBV(2, 3)), ctx.MkMul(b[4], ctx.MkBV(2, 4))), ctx.MkBV(5, 4)), ctx.MkBV(0, 1)),
ctx.MkEq(ctx.MkMod(ctx.MkAdd(b[0], ctx.MkMul(b[1], ctx.MkBV(2, 1)), ctx.MkMul(b[2], ctx.MkBV(2, 2)), ctx.MkMul(b[3], ctx.MkBV(2, 3)), ctx.MkMul(b[4], ctx.MkBV(2, 4))), ctx.MkBV(6, 4)), ctx.MkBV(0, 1))
)));
BoolExpr F = ctx.MkAnd(f1, f2);
英文:
How do I specify a modular artithmetic condition such as
f1 := (b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4 ≡ 1 (mod 5)) ∨
(b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4 ≡ 2 (mod 5)) ∨
(b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4 ≡ 3 (mod 5)) ∨
(b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4 ≡ 4 (mod 5));
f2 := (b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4 ≡ 1 (mod 7)) ∨
(b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4 ≡ 2 (mod 7)) ∨
(b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4 ≡ 3 (mod 7)) ∨
(b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4 ≡ 4 (mod 7)) ∨
(b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4 ≡ 5 (mod 7)) ∨
(b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4 ≡ 6 (mod 7));
F := f1 ∧ f2;
in Z3 SMT Solver?
In the above example
- ≡ is modular equivalence,
- (mod p) denotes modular arithmetic relative to p,
- "+","*" denote modular arithmetic operations addition and multiplication respectively,
- ∨ is the logical-or (disjunction) and
- ∧ is the logical-and (conjunction)
- b[] is an array of bits i.e., belongs to the set {0,1}
- 2^0, 2^1, ...., 2^4 are powers of 2
Z3 C# .NET API preferred. If not, any equivalent Z3 formulation is fine.
答案1
得分: 1
使用 Z3 .NET API
internal class Z3Demo : Context
{
public Z3Demo() :
// This example needs model generation turned on.
base(new Dictionary<string, string>() { { "model", "true" } })
{
}
public void Show()
{
void o(string s) { Console.WriteLine(s); }
Microsoft.Z3.Global.ToggleWarningMessages(true);
o($"Z3 版本: {Microsoft.Z3.Version.FullVersion}");
// 用于创建整数常量的本地辅助函数
IntExpr Int(int val) { return MkInt(val); }
var b = new BoolExpr[5];
for (int i = 0; i < b.Length; i++)
{
b[i] = MkBoolConst($"b{i}");
}
var operands = new IntExpr[5];
for (int i = 0; i < b.Length; i++)
{
operands[i] = (IntExpr)MkITE(b[i], Int(1 << i), Int(0));
}
var sum = (IntExpr)MkAdd(operands);
var sum5 = MkMod(sum, Int(5));
var sum7 = MkMod(sum, Int(7));
var f1 = MkNot(MkEq(sum5, Int(0)));
var f2 = MkNot(MkEq(sum7, Int(0)));
var F = MkAnd(f1, f2);
var solver = MkSolver();
solver.Assert(F);
if (solver.Check() == Status.SATISFIABLE)
{
o("找到解:");
for (int i = 0; i < b.Length; i++)
{
o($"b{i} = {solver.Model.Evaluate(b[i])}");
}
o("");
o($"{F}");
}
else
{
o("未找到解决方案");
}
}
}
使用 z3py Z3 Python API
from z3 import *
# 创建布尔决策变量的数组
b = [Bool(f'b{i}') for i in range(5)]
sum = Sum([b[i] * (2 ** i) for i in range(5)])
sum5 = sum % 5
sum7 = sum % 7
# f1 = Or(sum5 == 1, sum5 == 2, sum5 == 3, sum5 == 4)
f1 = (sum5 != 0)
# f2 = Or(sum7 == 1, sum7 == 2, sum7 == 3, sum7 == 4, sum7 == 5, sum7 == 6)
f2 = (sum7 != 0)
F = And(f1, f2)
s = Solver()
s.add(F)
print(s.check())
print(s.model())
print(s.sexpr)
英文:
Using Z3 .net API
internal class Z3Demo : Context
{
public Z3Demo() :
// This example needs model generation turned on.
base(new Dictionary<string, string>() { { "model", "true" } })
{
}
public void Show()
{
void o(string s) { Console.WriteLine(s); }
Microsoft.Z3.Global.ToggleWarningMessages(true);
o($"Z3 Version: {Microsoft.Z3.Version.FullVersion}");
// local helper to create int constants
IntExpr Int(int val) { return MkInt(val); }
var b = new BoolExpr[5];
for (int i = 0; i < b.Length; i++)
{
b[i] = MkBoolConst($"b{i}");
}
var operands = new IntExpr[5];
for (int i = 0; i < b.Length; i++)
{
operands[i] = (IntExpr)MkITE(b[i], Int(1 << i), Int(0));
}
var sum = (IntExpr)MkAdd(operands);
var sum5 = MkMod(sum, Int(5));
var sum7 = MkMod(sum, Int(7));
var f1 = MkNot(MkEq(sum5, Int(0)));
var f2 = MkNot(MkEq(sum7, Int(0)));
var F = MkAnd(f1, f2);
var solver = MkSolver();
solver.Assert(F);
if (solver.Check() == Status.SATISFIABLE)
{
o("Solution found:");
for (int i = 0; i < b.Length; i++)
{
o($"b{i} = {solver.Model.Evaluate(b[i])}");
}
o("");
o($"{F}");
}
else
{
o("No solution found");
}
}
}
Resulting output
Z3 Version: Z3 4.12.0.0
Solution found:
b0 = true
b1 = false
b2 = false
b3 = false
b4 = false
(let ((a!1 (+ (ite b0 1 0) (ite b1 2 0) (ite b2 4 0) (ite b3 8 0) (ite b4 16 0))))
(and (not (= (mod a!1 5) 0)) (not (= (mod a!1 7) 0))))
Using z3py Z3 Python API
from z3 import *
# create array of Boolean decision variables
b = [Bool(f'b{i}') for i in range(5)]
sum = Sum([b[i] * (2 ** i) for i in range(5)])
sum5 = sum % 5
sum7 = sum % 7
# f1 = Or(sum5 == 1, sum5 == 2, sum5 == 3, sum5 == 4)
f1 = (sum5 != 0)
# f2 = Or(sum7 == 1, sum7 == 2, sum7 == 3, sum7 == 4, sum7 == 5, sum7 == 6)
f2 = (sum7 != 0)
F = And(f1, f2)
s = Solver()
s.add(F)
print(s.check())
print(s.model())
print(s.sexpr)
Resulting output
sat
[b2 = False, b3 = False, b1 = True, b4 = False, b0 = False]
<bound method Solver.sexpr of [And((If(b0, 1, 0) +
If(b1, 2, 0) +
If(b2, 4, 0) +
If(b3, 8, 0) +
If(b4, 16, 0))%
5 !=
0,
(If(b0, 1, 0) +
If(b1, 2, 0) +
If(b2, 4, 0) +
If(b3, 8, 0) +
If(b4, 16, 0))%
7 !=
0)]>
For integer rather than Boolean variables, the result is:
sat
[b1 = 1, b4 = 0, b0 = 0, b2 = 1, b3 = 0]
<bound method Solver.sexpr of [And((b0*1 + b1*2 + b2*4 + b3*8 + b4*16)%5 != 0,
(b0*1 + b1*2 + b2*4 + b3*8 + b4*16)%7 != 0),
b0 >= 0,
b0 <= 1,
b1 >= 0,
b1 <= 1,
b2 >= 0,
b2 <= 1,
b3 >= 0,
b3 <= 1,
b4 >= 0,
b4 <= 1]>
答案2
得分: 1
看起来这里可以进行一些“心智”优化。请注意,SMT求解器在位向量推理方面非常擅长。看起来你的b
只是一个5位的位向量。表达式:
b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4
与b
是完全等价的,当你将b[i]
解释为位向量的第i位时,其中0为最不重要位。所以,不要像这样“粗暴”地处理它,只需让求解器在内部处理它。这样你编码会更容易,而求解器可以使用自定义技巧加快运行速度。
考虑到这一点,你的f1
表示b
不能被5
整除,而f2
表示它不能被7
整除。因此,你正在寻找所有5位的位向量,既不可被5整除,也不可被7整除。
SMTLib解决方案
有了这个重新表述,编码变得更容易。你可以在SMT-solver的通用语言SMTLib中编码它:
(set-logic QF_BV)
(declare-fun b () (_ BitVec 5))
(assert (distinct (bvurem b #b00101) #b00000))
(assert (distinct (bvurem b #b00111) #b00000))
(check-sat)
(get-value (b))
这将输出:
sat
((b #b11000))
即,在十进制中是24;这是一个符合你约束的值。
Haskell解决方案
SMTLib相对来说比较冗长和繁琐。还有很多其他与z3绑定的方法,我比较喜欢的是SBV,如果你是Haskell爱好者的话。在这种情况下,这实际上是一个一行代码的事情:
ghci> sat $ \(x :: SWord 5) -> x `sMod` 5 ./= 0 .&& x `sMod` 7 ./= 0
Satisfiable. Model:
s0 = 24 :: WordN 5
这些高级接口的好处是你也可以轻松地得到“所有解”。只需将sat
改为allSat
:
ghci> allSat $ \(x :: SWord 5) -> x `sMod` 5 ./= 0 .&& x `sMod` 7 ./= 0
Solution #1:
s0 = 22 :: WordN 5
Solution #2:
s0 = 6 :: WordN 5
... 省略其他解的行数 ...
Solution #21:
s0 = 24 :: WordN 5
Found 21 different solutions.
显然,有确切的21个解决方案符合你的问题。
Python解决方案
Python是所有人的最爱。虽然它有很多缺点,但它适合玩一下:
from z3 import *
b = BitVec('b', 5)
s = Solver()
s.add(And(URem(b, 5) != 0, URem(b, 7) != 0))
if s.check() == sat:
print(s.model())
else:
print("No solution!")
请注意使用URem
而不是典型的%
运算符:后者执行的是带符号的位向量算术,而这不是我们在这里想要的。这将输出:
[b = 26]
你可以迭代这个值以获得所有的解,参见Stack Overflow上的很多相关问题。
.NET解决方案
我没有.NET环境,也不喜欢发布我自己没有运行过的代码。但你可以使用Axel发布的框架将这个解决方案移植到.NET中。只需确保使用正确的模数运算符,即无符号位向量的运算符。
英文:
Looks like there's some "mental" optimizations you can do here. Note that SMT solvers are very good at bit-vector reasoning. Looks like your b
is simply a 5-bit bit-vector. The expression:
b[0]*2^0 + b[1]*2^1 + b[2]*2^2 + b[3]*2^3 + b[4]*2^4
is precisely equivalent to b
, when you interpret b[i]
as the i'th bit of the bit-vector, 0 being the least-significant. So, instead of "blasting" it like this, simply let the solver handle it internally. You'll have easier time coding, and the solver can use custom tricks to make it go faster.
With this in mind, your f1
says b
isn't divisible by 5
, and your f2
says it's not divisible by 7
. So, you're looking for all 5-bit bit-vectors, that are neither divisible by 5, nor by 7.
SMTLib solution
With this rephrasing, coding becomes much easy. You can code it in SMTLib, the lingua franca of SMT-solvers:
(set-logic QF_BV)
(declare-fun b () (_ BitVec 5))
(assert (distinct (bvurem b #b00101) #b00000))
(assert (distinct (bvurem b #b00111) #b00000))
(check-sat)
(get-value (b))
This prints:
sat
((b #b11000))
i.e., in decimal 24; a value that satisfies your constraints.
Haskell solution
SMTLib is rather verbose and baroque. There are many other bindings to z3, a favorite of mine is SBV, if you are a Haskell person. In this case, it's literally a one liner:
ghci> sat $ \(x :: SWord 5) -> x `sMod` 5 ./= 0 .&& x `sMod` 7 ./= 0
Satisfiable. Model:
s0 = 24 :: WordN 5
The good thing about these high-level interfaces is that you can easily get "all-solutions" as well. Simply change sat
to allSat
:
ghci> allSat $ \(x :: SWord 5) -> x `sMod` 5 ./= 0 .&& x `sMod` 7 ./= 0
Solution #1:
s0 = 22 :: WordN 5
Solution #2:
s0 = 6 :: WordN 5
... deleted lines for brevity ...
Solution #21:
s0 = 24 :: WordN 5
Found 21 different solutions.
Apparently there are exactly 21 solutions to your problem.
Python solution
Python is everybody's favorite. While it has a lot of shortcomings, it's good for playing around:
from z3 import *
b = BitVec('b', 5)
s = Solver()
s.add(And(URem(b, 5) != 0, URem(b, 7) != 0))
if s.check() == sat:
print(s.model())
else:
print("No solution!")
Note the use of URem
instead of the typical %
operator: The latter does signed bit-vector arithmetic, which is not what we want here. This prints:
[b = 26]
You can iterate over this value to get all the solutions as well, see stack-overflow for many questions along these lines.
.NET solution
I don't have a .NET environment and I hate to post code that I haven't run myself. But you can use the skeleton Axel posted to port this solution there too. Just make sure you use the correct modulus operator, i.e., one over the unsigned bit-vectors.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论