Reputation: 307
I would like to use a model's output (in my case sat and unsat only) in another model. Here, a model is a satisfying assignment to constants involved in a set of logical formulas (Z3 expressions in this case). My objective can be briefly explained as follows.
My problem can be described in detail as follows: I have a formalized a problem P, a set logical formulas (expressions) corresponding to a number of constraints (C). Among the expressions, one (e.g., Ai > 0) is my objective. Executing the model/formalization P returns sat, if all the constraints are satisfiable. Note that, Ai = 0 is always possible. Now, I want to find the set of assignments to a particular set of variables corresponds to the constraints (C) that ensure that Ai > 0 (for any i) is not possible. Currently, I am solving the problem by writing a program (in C#) that develop a DFS-based searching algorithm of the constraints (i.e., constraint values) and execute P to see if the result is false with the help of "push/pop". Though I have tried to make the search better, it does not help me a lot. It is very inefficient for a large problem size. It would be great if I could create another SMT program (model) utilizing P for searching such a satisfiable set.
The current formalization of problem P (a short SMT LIB 2 version of the original problem) is as follows:
(declare-fun th1 () Real)
(declare-fun th2 () Real)
(declare-fun th3 () Real)
(declare-fun th4 () Real)
(declare-fun th5 () Real)
(declare-fun l1 () Real)
(declare-fun l2 () Real)
(declare-fun l3 () Real)
(declare-fun l4 () Real)
(declare-fun l5 () Real)
(declare-fun l6 () Real)
(declare-fun l7 () Real)
(declare-fun p1 () Real)
(declare-fun p2 () Real)
(declare-fun p3 () Real)
(declare-fun p4 () Real)
(declare-fun p5 () Real)
(declare-fun sl1 () Int)
(declare-fun sl2 () Int)
(declare-fun sl3 () Int)
(declare-fun sl4 () Int)
(declare-fun sl5 () Int)
(declare-fun sl6 () Int)
(declare-fun sl7 () Int)
(declare-fun sp1 () Int)
(declare-fun sp2 () Int)
(declare-fun sp3 () Int)
(declare-fun sp4 () Int)
(declare-fun sp5 () Int)
(declare-fun a1 () Int)
(declare-fun a2 () Int)
(declare-fun a3 () Int)
(declare-fun a4 () Int)
(declare-fun a5 () Int)
(declare-fun na () Int)
(declare-fun ns () Int)
(declare-fun attack () Bool)
;;;; System
(assert (and (= l1 (* (- th2 th1) 17.0))
(= l2 (* (- th5 th1) 4.5))
(= l3 (* (- th3 th2) 5.05))
(= l4 (* (- th4 th2) 5.65))
(= l5 (* (- th5 th2) 5.75))
(= l6 (* (- th4 th3) 5.85))
(= l7 (* (- th5 th4) 23.75))
(= p1 (+ l1 l2))
(= p2 (+ l1 l3 l4 l5))
(= p3 (+ l3 l6))
(= p4 (+ l4 l6 l7))
(= p5 (+ l2 l5 l7))
)
)
;;;; Secured measurements
(assert (and (or (= sl1 0) (= sl1 1))
(or (= sl2 0) (= sl2 1))
(or (= sl3 0) (= sl3 1))
(or (= sl4 0) (= sl4 1))
(or (= sl5 0) (= sl5 1))
(or (= sl6 0) (= sl6 1))
(or (= sl7 0) (= sl7 1))
(or (= sp1 0) (= sp1 1))
(or (= sp2 0) (= sp2 1))
(or (= sp3 0) (= sp3 1))
(or (= sp4 0) (= sp4 1))
(or (= sp5 0) (= sp5 1))
)
)
(assert (and (=> (not (= l1 0.0)) (= sl1 0))
(=> (not (= l2 0.0)) (= sl2 0))
(=> (not (= l3 0.0)) (= sl3 0))
(=> (not (= l4 0.0)) (= sl4 0))
(=> (not (= l5 0.0)) (= sl5 0))
(=> (not (= l6 0.0)) (= sl6 0))
(=> (not (= l7 0.0)) (= sl7 0))
(=> (not (= p1 0.0)) (= sp1 0))
(=> (not (= p2 0.0)) (= sp2 0))
(=> (not (= p3 0.0)) (= sp3 0))
(=> (not (= p4 0.0)) (= sp4 0))
(=> (not (= p5 0.0)) (= sp5 0))
)
)
(assert (and (= sl1 1) (= sl2 1)))
;;;; Attacks
(assert (and (or (= a1 0) (= a1 1))
(or (= a2 0) (= a2 1))
(or (= a3 0) (= a3 1))
(or (= a4 0) (= a4 1))
(or (= a5 0) (= a5 1))
)
)
(assert (and
(= (not (= th1 0.0)) (= a1 1))
(= (not (= th2 0.0)) (= a2 1))
(= (not (= th3 0.0)) (= a3 1))
(= (not (= th4 0.0)) (= a4 1))
(= (not (= th5 0.0)) (= a5 1))
)
)
(assert (= th1 0.0)) // Base condition
(assert (= na (+ a1 a2 a3 a4 a5)))
(assert (=> attack (> na 1)))
;;;; Check for satisfiable model
(assert attack)
(check-sat)
(get-model)
(exit)
I want synthesize the security measurements (i.e., find the assignments of 'sl' and 'sp' terms) so that there would be no attack (i.e., na will be 0) given the constraints, e.g., as follows:
(assert (= ns (+ sl1 sl2 sl3 sl4 sl5 sl6 sl7 sp1 sp2 sp3 sp4 sp5)))
(assert (<= ns 4))
In this case, the assertion (i.e., '(assert (and (= sl1 1) (= sl2 1)))' ) will be commented. Currently, I have developed a C# program that take an assignment of 'sl' and 'sp', assert them like (assert (and (= sl1 1) (= sl2 1) ...))', and execute the given program to see if there is any attack possible. I am done when the program returns unsat (i.e., na > 1 is not possible). Is there any way to solve the problem using SMT (Z3) only?
Upvotes: 2
Views: 431
Reputation: 2884
Thanks for clearing up the question. If I've understood things, you can perform the search for sli
and spj
values using Z3, but you cannot do this using SMT-LIB only, you need to use an API. The idea is to use the models (satisfying assignments) from one sat check as constraints in future checks, as explained in detail in these answers:
Z3: finding all satisfying models
(Z3Py) checking all solutions for equation
Here's your example encoded in the Python API (z3py link: http://rise4fun.com/Z3Py/KHzm):
s = Solver()
th1, th2, th3, th4, th5 = Reals('th1 th2 th3 th4 th5')
th = { 'th1' : th1, 'th2' : th2, 'th3' : th3, 'th4' : th4, 'th5' : th5}
l1, l2, l3, l4, l5, l6, l7 = Reals('l1 l2 l3 l4 l5 l6 l7')
l = { 'l1' : l1, 'l2' : l2, 'l3' : l3, 'l4' : l4, 'l5' : l5, 'l6' : l6, 'l7' : l7 }
p1, p2, p3, p4, p5 = Reals('p1 p2 p3 p4 p5')
p = { 'p1' : p1, 'p2' : p2, 'p3' : p3, 'p4' : p4, 'p5' : p5 }
sl1, sl2, sl3, sl4, sl5, sl6, sl7 = Ints('sl1 sl2 sl3 sl4 sl5 sl6 sl7')
sl = { 'sl1' : sl1, 'sl2' : sl2, 'sl3' : sl3, 'sl4' : sl4, 'sl5' : sl5, 'sl6' : sl6, 'sl7' : sl7 }
sp1, sp2, sp3, sp4, sp5 = Ints('sp1 sp2 sp3 sp4 sp5')
sp = { 'sp1' : sp1, 'sp2' : sp2, 'sp3' : sp3, 'sp4' : sp4, 'sp5' : sp5 }
a1, a2, a3, a4, a5 = Ints('a1 a2 a3 a4 a5')
a = { 'a1' : a1, 'a2' : a2, 'a3' : a3, 'a4' : a4, 'a5' : a5 }
na, ns = Ints('na ns')
attack = Bool('attack')
n = { 'na' : na, 'ns' : ns, 'attack' : attack}
dict_decl = dict(th.items() + l.items() + p.items() + sl.items() + sp.items() + a.items() + n.items() )
assertions = []
assertions.append(parse_smt2_string('(assert (and (= l1 (* (- th2 th1) 17.0)) (= l2 (* (- th5 th1) 4.5)) (= l3 (* (- th3 th2) 5.05)) (= l4 (* (- th4 th2) 5.65)) (= l5 (* (- th5 th2) 5.75)) (= l6 (* (- th4 th3) 5.85)) (= l7 (* (- th5 th4) 23.75)) (= p1 (+ l1 l2)) (= p2 (+ l1 l3 l4 l5)) (= p3 (+ l3 l6)) (= p4 (+ l4 l6 l7)) (= p5 (+ l2 l5 l7))))', decls=dict_decl))
assertions.append(parse_smt2_string('(assert (and (or (= sl1 0) (= sl1 1)) (or (= sl2 0) (= sl2 1)) (or (= sl3 0) (= sl3 1)) (or (= sl4 0) (= sl4 1)) (or (= sl5 0) (= sl5 1)) (or (= sl6 0) (= sl6 1)) (or (= sl7 0) (= sl7 1)) (or (= sp1 0) (= sp1 1)) (or (= sp2 0) (= sp2 1)) (or (= sp3 0) (= sp3 1)) (or (= sp4 0) (= sp4 1)) (or (= sp5 0) (= sp5 1)) ))', decls=dict_decl))
assertions.append(parse_smt2_string('(assert (and (=> (not (= l1 0.0)) (= sl1 0)) (=> (not (= l2 0.0)) (= sl2 0)) (=> (not (= l3 0.0)) (= sl3 0)) (=> (not (= l4 0.0)) (= sl4 0)) (=> (not (= l5 0.0)) (= sl5 0)) (=> (not (= l6 0.0)) (= sl6 0)) (=> (not (= l7 0.0)) (= sl7 0)) (=> (not (= p1 0.0)) (= sp1 0)) (=> (not (= p2 0.0)) (= sp2 0)) (=> (not (= p3 0.0)) (= sp3 0)) (=> (not (= p4 0.0)) (= sp4 0)) (=> (not (= p5 0.0)) (= sp5 0)) ))', decls=dict_decl))
assertions.append(parse_smt2_string('(assert (and (= sl1 1) (= sl2 1)))', decls=dict_decl))
assertions.append(parse_smt2_string('(assert (and (or (= a1 0) (= a1 1))(or (= a2 0) (= a2 1))(or (= a3 0) (= a3 1))(or (= a4 0) (= a4 1))(or (= a5 0) (= a5 1)) ))', decls=dict_decl))
assertions.append(parse_smt2_string('(assert (and (= (not (= th1 0.0)) (= a1 1))(= (not (= th2 0.0)) (= a2 1))(= (not (= th3 0.0)) (= a3 1))(= (not (= th4 0.0)) (= a4 1))(= (not (= th5 0.0)) (= a5 1)) ))', decls=dict_decl))
assertions.append(parse_smt2_string('(assert (= ns (+ sl1 sl2 sl3 sl4 sl5 sl6 sl7 sp1 sp2 sp3 sp4 sp5)))', decls=dict_decl))
assertions.append(parse_smt2_string('(assert (<= ns 4))', decls=dict_decl))
#assertions.append(parse_smt2_string('(assert (and (= sl1 1) (= sl2 1)))', decls=dict_decl)) # commented as suggested
assertions.append(parse_smt2_string('(assert (= th1 0.0))', decls=dict_decl))
assertions.append(parse_smt2_string('(assert (= na (+ a1 a2 a3 a4 a5)))', decls=dict_decl))
assertions.append(parse_smt2_string('(assert (=> attack (> na 1)))', decls=dict_decl))
assertions.append(parse_smt2_string('(assert attack)', decls=dict_decl))
print assertions
s.add(assertions)
synthesized = []
iters = 0
while s.check() == sat:
print "Iteration " + str(iters)
print s.model()
avoid = []
# key step: add constraint to prevent any values assigned (if possible) to constants from being equal to their satisfying assignments (models) in this sat iteration
for sli in sl.values():
avoid.append(sli != s.model()[sli])
for spi in sp.values():
avoid.append(spi != s.model()[spi])
s.add(Or(avoid))
# end key step
synthesized.append(avoid)
print avoid
iters = iters + 1
# unless you know how to guarantee termination (e.g., there is a constraint ensuring the slis and spis take values in finite sets)
if iters >= 1000:
break
print "Done"
print synthesized # all the constraints
Apologies for all the constants and numbers, I just used the quickest translation of your SMT-LIB script, but it ended up being rather cumbersome, I would use iterators everywhere. This generated the following constraints over the sli
and spj
constants:
[[sl4 ≠ 0, sl5 ≠ 0, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 0, sp2 ≠ 0, sp3 ≠ 0, sp4 ≠ 1, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 0, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 0, sp2 ≠ 0, sp3 ≠ 0, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 1, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 0, sp2 ≠ 0, sp3 ≠ 0, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 1, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 0, sp2 ≠ 0, sp3 ≠ 1, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 1, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 0, sp2 ≠ 1, sp3 ≠ 0, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 1, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 1, sp2 ≠ 0, sp3 ≠ 0, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 0, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 1, sp2 ≠ 0, sp3 ≠ 1, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 0, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 0, sp2 ≠ 0, sp3 ≠ 1, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 0, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 1, sp2 ≠ 0, sp3 ≠ 0, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 0, sl6 ≠ 1, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 1, sp2 ≠ 0, sp3 ≠ 0, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 0, sl6 ≠ 1, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 0, sp2 ≠ 0, sp3 ≠ 0, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 1, sl6 ≠ 1, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 0, sp2 ≠ 0, sp3 ≠ 0, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 0, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 1, sp2 ≠ 0, sp3 ≠ 0, sp4 ≠ 1, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 1, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 0, sp2 ≠ 0, sp3 ≠ 0, sp4 ≠ 1, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 0, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 0, sp2 ≠ 1, sp3 ≠ 0, sp4 ≠ 0, sp5 ≠ 0], [sl4 ≠ 0, sl5 ≠ 0, sl6 ≠ 0, sl7 ≠ 0, sl1 ≠ 1, sl2 ≠ 1, sl3 ≠ 0, sp1 ≠ 1, sp2 ≠ 1, sp3 ≠ 0, sp4 ≠ 0, sp5 ≠ 0]]
Upvotes: 3
Reputation: 3688
If I understand this correctly, then you are precisely looking for (universal) quantification. Excuse my pseudo-notation, but are you not looking for a satisfying assignment to the free variables (config_params
) in the following?
config_constraints(config_params) -> forall attack_params: not attack_constraints(attack_params, config_params)
where the ()
notation merely indicates which variable (sets) the constraints depend upon. I'm pretty sure that quantifiers are supported in the .Net API, as they are in the Java API.
Upvotes: 2