mirror of
https://github.com/Z3Prover/z3
synced 2026-05-25 03:16:21 +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
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue