Could you like to tell how to split clauses of unsat cores? And here is question 2 regarding after found out unsat cores, I will try to seek again. Would you like to tell how to
I'm not sure if I understood your question. It seems you have an assertion of the form (and c1 (and c2 c3))
, and you want to track c1
, c2
and c3
individually.
In Z3, we use answer literals to track assertions. An answer literal is essentially a fresh Boolean that is used to track an assertion. That is, whether the assertion was used (by Z3) to show unsatisfiability of the whole set of assertions or not. For example, if we want to track assertion F
, we create a fresh Boolean variable p
and assert p implies F
. Then, we provide p
as an argument for the check method.
If F
is a big conjunction and we want to track its elements individually, we should extract its elements and create an answer literal for each one of them. Here is the complete example that does the trick. You can test it by including it in the example.cpp
file that is included in the Z3 distribution. Note that you have to include #include
.
/**
\brief Unsat core example 2
*/
void unsat_core_example2() {
std::cout << "unsat core example 2\n";
context c;
// The answer literal mechanism, described in the previous example,
// tracks assertions. An assertion can be a complicated
// formula containing containing the conjunction of many subformulas.
expr p1 = c.bool_const("p1");
expr x = c.int_const("x");
expr y = c.int_const("y");
solver s(c);
expr F = x > 10 && y > x && y < 5 && y > 0;
s.add(implies(p1, F));
expr assumptions[1] = { p1 };
std::cout << s.check(1, assumptions) << "\n";
expr_vector core = s.unsat_core();
std::cout << core << "\n";
std::cout << "size: " << core.size() << "\n";
for (unsigned i = 0; i < core.size(); i++) {
std::cout << core[i] << "\n";
}
// The core is not very informative, since p1 is tracking the formula F
// that is a conjunction of subformulas.
// Now, we use the following piece of code to break this conjunction
// into individual subformulas. First, we flat the conjunctions by
// using the method simplify.
std::vector qs; // auxiliary vector used to store new answer literals.
assert(F.is_app()); // I'm assuming F is an application.
if (F.decl().decl_kind() == Z3_OP_AND) {
// F is a conjunction
std::cout << "F num. args (before simplify): " << F.num_args() << "\n";
F = F.simplify();
std::cout << "F num. args (after simplify): " << F.num_args() << "\n";
for (unsigned i = 0; i < F.num_args(); i++) {
std::cout << "Creating answer literal q" << i << " for " << F.arg(i) << "\n";
std::stringstream qname; qname << "q" << i;
expr qi = c.bool_const(qname.str().c_str()); // create a new answer literal
s.add(implies(qi, F.arg(i)));
qs.push_back(qi);
}
}
// The solver s already contains p1 => F
// To disable F, we add (not p1) as an additional assumption
qs.push_back(!p1);
std::cout << s.check(qs.size(), &qs[0]) << "\n";
expr_vector core2 = s.unsat_core();
std::cout << core2 << "\n";
std::cout << "size: " << core2.size() << "\n";
for (unsigned i = 0; i < core2.size(); i++) {
std::cout << core2[i] << "\n";
}
}