mirror of
https://github.com/Z3Prover/z3
synced 2026-04-02 09:58:59 +00:00
Properly extract justifications from subsolver
This commit is contained in:
parent
e25e93503b
commit
36b01a51f1
6 changed files with 74 additions and 38 deletions
|
|
@ -32,7 +32,7 @@ namespace smt {
|
|||
*/
|
||||
class context_solver : public seq::simple_solver {
|
||||
smt_params m_params; // must be declared before m_kernel
|
||||
smt::kernel m_kernel;
|
||||
kernel m_kernel;
|
||||
arith_value m_arith_value;
|
||||
|
||||
static smt_params make_seq_len_params() {
|
||||
|
|
@ -55,6 +55,10 @@ namespace smt {
|
|||
// std::cout << "\t" << mk_pp(m_kernel.get_formula(i), m_kernel.m()) << std::endl;
|
||||
// }
|
||||
// std::cout << std::endl;
|
||||
// std::cout << "Checking" << std::endl;
|
||||
// for (unsigned i = 0; i < m_kernel.size(); i++) {
|
||||
// std::cout << mk_pp(m_kernel.get_formula(i), m_kernel.m()) << std::endl;
|
||||
// }
|
||||
return m_kernel.check();
|
||||
}
|
||||
|
||||
|
|
@ -84,14 +88,20 @@ namespace smt {
|
|||
return m_arith_value.get_up(e, hi, is_strict) && !is_strict && hi.is_int();
|
||||
}
|
||||
|
||||
virtual expr_ref_vector get_unsat_core() {
|
||||
lbool check_with_assumptions(expr_ref_vector& assumptions, expr_ref_vector& core) override {
|
||||
// TODO: Not ideal
|
||||
// Replay with assumptions
|
||||
// std::cout << "Assuming" << std::endl;
|
||||
// for (unsigned i = 0; i < assumptions.size(); i++) {
|
||||
// std::cout << mk_pp(assumptions[i].get(), m_kernel.m()) << std::endl;
|
||||
// }
|
||||
lbool r = m_kernel.check(assumptions.size(), assumptions.data());
|
||||
unsigned cnt = m_kernel.get_unsat_core_size();
|
||||
expr_ref_vector core(m_kernel.m());
|
||||
core.reserve(cnt);
|
||||
core.resize(cnt);
|
||||
for (unsigned i = 0; i < cnt; i++) {
|
||||
core[i] = m_kernel.get_unsat_core_expr(i);
|
||||
}
|
||||
return core;
|
||||
return r;
|
||||
}
|
||||
|
||||
void reset() override {
|
||||
|
|
|
|||
|
|
@ -243,6 +243,12 @@ namespace seq {
|
|||
}
|
||||
|
||||
void nielsen_node::add_constraint(constraint const &c) {
|
||||
if (graph().get_manager().is_and(c.fml)) {
|
||||
// this is important, as the subsolver might decompose for returned unsat cores
|
||||
for (unsigned i = 0; i < to_app(c.fml)->get_num_args(); ++i) {
|
||||
add_constraint(constraint(to_app(c.fml)->get_arg(i), c.dep, graph().get_manager()));
|
||||
}
|
||||
}
|
||||
m_constraints.push_back(c);
|
||||
if (m_graph.m_literal_if_false) {
|
||||
auto lit = m_graph.m_literal_if_false(c.fml);
|
||||
|
|
@ -386,11 +392,12 @@ namespace seq {
|
|||
// nielsen_graph
|
||||
// -----------------------------------------------
|
||||
|
||||
nielsen_graph::nielsen_graph(euf::sgraph &sg, simple_solver &solver):
|
||||
nielsen_graph::nielsen_graph(euf::sgraph &sg, simple_solver &solver, simple_solver &core_solver):
|
||||
m(sg.get_manager()),
|
||||
m_seq(sg.get_seq_util()),
|
||||
m_sg(sg),
|
||||
m_solver(solver),
|
||||
m_core_solver(core_solver),
|
||||
m_parikh(alloc(seq_parikh, sg)),
|
||||
m_seq_regex(alloc(seq::seq_regex, sg)),
|
||||
m_len_vars(sg.get_manager()),
|
||||
|
|
@ -495,6 +502,7 @@ namespace seq {
|
|||
m_gpower_vars.reset();
|
||||
m_dep_mgr.reset();
|
||||
m_solver.reset();
|
||||
m_core_solver.reset();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
|
@ -1268,7 +1276,7 @@ namespace seq {
|
|||
vector<length_constraint> constraints;
|
||||
generate_length_constraints(constraints);
|
||||
for (auto const& lc : constraints) {
|
||||
m_solver.assert_expr(lc.m_expr);
|
||||
m_root->add_constraint(lc.to_constraint());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3916,15 +3924,20 @@ namespace seq {
|
|||
}
|
||||
|
||||
dep_tracker nielsen_graph::get_subsolver_dependency(nielsen_node* n) {
|
||||
auto core = m_solver.get_unsat_core();
|
||||
SASSERT(!core.empty());
|
||||
u_map<dep_tracker> expr_to_dep;
|
||||
for (unsigned i = 0; i < n->m_parent_ic_count; i++) {
|
||||
expr_ref_vector assumptions(m);
|
||||
assumptions.resize(n->constraints().size());
|
||||
for (unsigned i = 0; i < assumptions.size(); i++) {
|
||||
auto& c = n->constraints()[i];
|
||||
expr_to_dep.insert_if_not_there(c.fml->get_id(), c.dep);
|
||||
assumptions[i] = c.fml;
|
||||
}
|
||||
expr_ref_vector core(m);
|
||||
lbool res = m_core_solver.check_with_assumptions(assumptions, core);
|
||||
VERIFY(res == l_false);
|
||||
|
||||
dep_tracker dep = dep_mgr().mk_empty();
|
||||
for (const expr * const e : core) {
|
||||
for (expr* e : core) {
|
||||
dep = dep_mgr().mk_join(dep, expr_to_dep[e->get_id()]);
|
||||
}
|
||||
return dep;
|
||||
|
|
@ -3996,8 +4009,9 @@ namespace seq {
|
|||
IF_VERBOSE(1, verbose_stream() << "solve_sat_path_ints: sat_path length=" << m_sat_path.size() << "\n";);
|
||||
m_solver.push();
|
||||
for (nielsen_edge* e : m_sat_path) {
|
||||
for (auto const& ic : e->side_constraints())
|
||||
for (auto const& ic : e->side_constraints()) {
|
||||
m_solver.assert_expr(ic.fml);
|
||||
}
|
||||
}
|
||||
if (m_sat_node) {
|
||||
for (auto const& ic : m_sat_node->constraints())
|
||||
|
|
|
|||
|
|
@ -273,12 +273,12 @@ namespace seq {
|
|||
class simple_solver {
|
||||
public:
|
||||
virtual ~simple_solver() {}
|
||||
virtual lbool check() = 0;
|
||||
virtual void assert_expr(expr* e) = 0;
|
||||
virtual void push() = 0;
|
||||
virtual void pop(unsigned num_scopes) = 0;
|
||||
virtual void get_model(model_ref& mdl) { mdl = nullptr; }
|
||||
virtual expr_ref_vector get_unsat_core() { throw z3_exception(); }
|
||||
virtual lbool check() = 0;
|
||||
virtual void assert_expr(expr* e) = 0;
|
||||
virtual void push() = 0;
|
||||
virtual void pop(unsigned num_scopes) = 0;
|
||||
virtual void get_model(model_ref& mdl) { mdl = nullptr; }
|
||||
virtual lbool check_with_assumptions(expr_ref_vector& assumptions, expr_ref_vector& core) { return l_undef; }
|
||||
// Optional bound queries on arithmetic expressions (non-strict integer bounds).
|
||||
// Default implementation reports "unsupported".
|
||||
virtual bool lower_bound(expr* e, rational& lo) const { return false; }
|
||||
|
|
@ -439,24 +439,6 @@ namespace seq {
|
|||
}
|
||||
};
|
||||
|
||||
// kind of length constraint determines propagation strategy
|
||||
enum class length_kind {
|
||||
nonneg, // len(x) >= 0: unconditional axiom
|
||||
eq, // len(lhs) = len(rhs): conditional on string equality
|
||||
bound // Parikh bound: conditional on regex membership
|
||||
};
|
||||
|
||||
// arithmetic length constraint derived from string equations
|
||||
struct length_constraint {
|
||||
expr_ref m_expr; // arithmetic expression (e.g., len(x) + len(y) = len(a) + 1)
|
||||
dep_tracker m_dep; // tracks which input constraints contributed
|
||||
length_kind m_kind; // determines propagation strategy
|
||||
|
||||
length_constraint(ast_manager& m): m_expr(m), m_dep(nullptr), m_kind(length_kind::nonneg) {}
|
||||
length_constraint(expr* e, dep_tracker const& dep, length_kind kind, ast_manager& m):
|
||||
m_expr(e, m), m_dep(dep), m_kind(kind) {}
|
||||
};
|
||||
|
||||
// -----------------------------------------------
|
||||
// integer constraint: equality or inequality over length expressions
|
||||
// mirrors ZIPT's IntEq and IntLe
|
||||
|
|
@ -476,6 +458,29 @@ namespace seq {
|
|||
|
||||
std::ostream& display(std::ostream& out) const;
|
||||
};
|
||||
|
||||
// kind of length constraint determines propagation strategy
|
||||
enum class length_kind {
|
||||
nonneg, // len(x) >= 0: unconditional axiom
|
||||
eq, // len(lhs) = len(rhs): conditional on string equality
|
||||
bound // Parikh bound: conditional on regex membership
|
||||
};
|
||||
|
||||
// arithmetic length constraint derived from string equations
|
||||
struct length_constraint {
|
||||
expr_ref m_expr; // arithmetic expression (e.g., len(x) + len(y) = len(a) + 1)
|
||||
dep_tracker m_dep; // tracks which input constraints contributed
|
||||
length_kind m_kind; // determines propagation strategy
|
||||
|
||||
length_constraint(ast_manager& m): m_expr(m), m_dep(nullptr), m_kind(length_kind::nonneg) {}
|
||||
length_constraint(expr* e, dep_tracker const& dep, length_kind kind, ast_manager& m):
|
||||
m_expr(e, m), m_dep(dep), m_kind(kind) {}
|
||||
|
||||
constraint to_constraint() const {
|
||||
return constraint(m_expr, m_dep, m_expr.get_manager());
|
||||
}
|
||||
};
|
||||
|
||||
// edge in the Nielsen graph connecting two nodes
|
||||
// mirrors ZIPT's NielsenEdge
|
||||
class nielsen_edge {
|
||||
|
|
@ -763,6 +768,7 @@ namespace seq {
|
|||
// and optimistically assumes feasibility.
|
||||
// -----------------------------------------------
|
||||
simple_solver& m_solver;
|
||||
simple_solver& m_core_solver;
|
||||
|
||||
// Constraint.Shared: guards re-assertion of root-level constraints.
|
||||
// Set to true after assert_root_constraints_to_solver() is first called.
|
||||
|
|
@ -820,7 +826,7 @@ namespace seq {
|
|||
public:
|
||||
// Construct with a caller-supplied solver. Ownership is NOT transferred;
|
||||
// the caller is responsible for keeping the solver alive.
|
||||
nielsen_graph(euf::sgraph& sg, simple_solver& solver);
|
||||
nielsen_graph(euf::sgraph& sg, simple_solver& solver, simple_solver &core_solver);
|
||||
~nielsen_graph();
|
||||
|
||||
void set_literal_if_false(std::function<sat::literal(expr* e)> const& lif) {
|
||||
|
|
|
|||
|
|
@ -93,6 +93,10 @@ namespace smt {
|
|||
return m_imp->m_kernel.get_num_asserted_formulas();
|
||||
}
|
||||
|
||||
void kernel::get_formulas(ptr_vector<expr>& r) const {
|
||||
return m_imp->m_kernel.get_asserted_formulas(r);
|
||||
}
|
||||
|
||||
expr* kernel::get_formula(unsigned i) const {
|
||||
return m_imp->m_kernel.get_asserted_formula(i);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ namespace smt {
|
|||
m_egraph(m),
|
||||
m_sgraph(m, m_egraph),
|
||||
m_context_solver(m),
|
||||
m_nielsen(m_sgraph, m_context_solver),
|
||||
m_core_solver(m),
|
||||
m_nielsen(m_sgraph, m_context_solver, m_core_solver),
|
||||
m_axioms(m_th_rewriter),
|
||||
m_regex(m_sgraph),
|
||||
m_model(m, m_seq, m_rewriter, m_sgraph),
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ namespace smt {
|
|||
// m_context_solver must be declared before m_nielsen: its address is passed
|
||||
// to the m_nielsen constructor and must remain stable for the object's lifetime.
|
||||
context_solver m_context_solver;
|
||||
context_solver m_core_solver;
|
||||
seq::nielsen_graph m_nielsen;
|
||||
seq::axioms m_axioms;
|
||||
seq::seq_regex m_regex; // regex membership pre-processing
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue