mirror of
https://github.com/Z3Prover/z3
synced 2026-05-20 17:09:35 +00:00
split into context and sub-solver, move length force predicates to context-solver
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
9d4feed0ae
commit
2a36b9a68e
9 changed files with 441 additions and 240 deletions
|
|
@ -45,10 +45,9 @@ namespace smt {
|
||||||
*
|
*
|
||||||
* Assertions with dep == nullptr are asserted directly (always active).
|
* Assertions with dep == nullptr are asserted directly (always active).
|
||||||
*/
|
*/
|
||||||
class context_solver : public seq::simple_solver {
|
class sub_solver : public seq::sub_solver_i {
|
||||||
smt_params m_params; // must be declared before m_kernel
|
smt_params m_params; // must be declared before m_kernel
|
||||||
kernel m_kernel;
|
kernel m_kernel;
|
||||||
arith_value m_arith_value;
|
|
||||||
|
|
||||||
// Tracked assumption literals.
|
// Tracked assumption literals.
|
||||||
// m_assump_lits[i] and m_frame_bounds together encode a stack of
|
// m_assump_lits[i] and m_frame_bounds together encode a stack of
|
||||||
|
|
@ -69,12 +68,10 @@ namespace smt {
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
context_solver(ast_manager& m) :
|
sub_solver(ast_manager& m) :
|
||||||
m_params(make_seq_len_params()),
|
m_params(make_seq_len_params()),
|
||||||
m_kernel(m, m_params),
|
m_kernel(m, m_params),
|
||||||
m_arith_value(m),
|
|
||||||
m_assump_lits(m) {
|
m_assump_lits(m) {
|
||||||
m_arith_value.init(&m_kernel.get_context());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lbool check() override {
|
lbool check() override {
|
||||||
|
|
@ -137,23 +134,8 @@ namespace smt {
|
||||||
|
|
||||||
seq::dep_tracker core() override { return m_last_core; }
|
seq::dep_tracker core() override { return m_last_core; }
|
||||||
|
|
||||||
bool lower_bound(expr* e, rational& lo, literal_vector& lits, enode_pair_vector& eqs) const override {
|
|
||||||
bool is_strict = true;
|
|
||||||
return m_arith_value.get_lo(e, lo, is_strict, lits, eqs) && !is_strict && lo.is_int();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool upper_bound(expr* e, rational& hi, literal_vector& lits, enode_pair_vector& eqs) const override {
|
|
||||||
bool is_strict = true;
|
|
||||||
return m_arith_value.get_up(e, hi, is_strict, lits, eqs) && !is_strict && hi.is_int();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool current_value(expr* e, rational& v) const override {
|
|
||||||
return m_arith_value.get_value(e, v) && v.is_int();
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() override {
|
void reset() override {
|
||||||
m_kernel.reset();
|
m_kernel.reset();
|
||||||
m_arith_value.init(&m_kernel.get_context());
|
|
||||||
m_assump_lits.reset();
|
m_assump_lits.reset();
|
||||||
m_assump_lit2id.reset();
|
m_assump_lit2id.reset();
|
||||||
m_frame_bounds.reset();
|
m_frame_bounds.reset();
|
||||||
|
|
@ -163,4 +145,55 @@ namespace smt {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class context_solver : public seq::context_solver_i {
|
||||||
|
smt::context &ctx;
|
||||||
|
arith_value m_arith_value;
|
||||||
|
public:
|
||||||
|
context_solver(smt::context& ctx):
|
||||||
|
ctx(ctx),
|
||||||
|
m_arith_value(ctx.get_manager()) {
|
||||||
|
m_arith_value.init(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lower_bound(expr *e, rational &lo, literal_vector &lits, enode_pair_vector &eqs) const override {
|
||||||
|
bool is_strict = true;
|
||||||
|
return m_arith_value.get_lo(e, lo, is_strict, lits, eqs) && !is_strict && lo.is_int();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool upper_bound(expr *e, rational &hi, literal_vector &lits, enode_pair_vector &eqs) const override {
|
||||||
|
bool is_strict = true;
|
||||||
|
return m_arith_value.get_up(e, hi, is_strict, lits, eqs) && !is_strict && hi.is_int();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool current_value(expr *e, rational &v) const override {
|
||||||
|
return m_arith_value.get_value(e, v) && v.is_int();
|
||||||
|
}
|
||||||
|
|
||||||
|
sat::literal literal_if_false(expr *e) {
|
||||||
|
bool is_not = ctx.get_manager().is_not(e, e);
|
||||||
|
if (m_should_internalize && !ctx.b_internalized(e)) {
|
||||||
|
// it can happen that the element is not internalized, but as soon as we do it, it becomes false.
|
||||||
|
// In case we just skip one of those uninternalized expressions,
|
||||||
|
// adding the Nielsen assumption later will fail
|
||||||
|
// Alternatively, we could just retry Nielsen saturation in case
|
||||||
|
// adding the Nielsen assumption yields the assumption being false after internalizing
|
||||||
|
ctx.internalize(to_app(e), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.b_internalized(e))
|
||||||
|
return sat::null_literal;
|
||||||
|
|
||||||
|
literal lit = ctx.get_literal(e);
|
||||||
|
if (is_not)
|
||||||
|
lit.neg();
|
||||||
|
if (ctx.get_assignment(lit) == l_false) {
|
||||||
|
// TRACE(seq, tout << "literal_if_false: " << lit << " " << mk_pp(e, m) << " is assigned false\n");
|
||||||
|
return lit;
|
||||||
|
}
|
||||||
|
// TRACE(seq, tout << "literal_if_false: " << mk_pp(e, m) << " is assigned " << ctx.get_assignment(lit) <<
|
||||||
|
// "\n");
|
||||||
|
return sat::null_literal;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -284,7 +284,7 @@ namespace seq {
|
||||||
enode_pair_vector eqs;
|
enode_pair_vector eqs;
|
||||||
if (m_graph.a.is_numeral(e, lo))
|
if (m_graph.a.is_numeral(e, lo))
|
||||||
return true;
|
return true;
|
||||||
if (!m_graph.m_solver.lower_bound(e, lo, lits, eqs))
|
if (!m_graph.m_context_solver.lower_bound(e, lo, lits, eqs))
|
||||||
return false;
|
return false;
|
||||||
for (auto lit : lits)
|
for (auto lit : lits)
|
||||||
dep = m_graph.dep_mgr().mk_join(dep, m_graph.dep_mgr().mk_leaf(lit));
|
dep = m_graph.dep_mgr().mk_join(dep, m_graph.dep_mgr().mk_leaf(lit));
|
||||||
|
|
@ -301,7 +301,7 @@ namespace seq {
|
||||||
enode_pair_vector eqs;
|
enode_pair_vector eqs;
|
||||||
if (m_graph.a.is_numeral(e, up))
|
if (m_graph.a.is_numeral(e, up))
|
||||||
return true;
|
return true;
|
||||||
if (!m_graph.m_solver.upper_bound(e, up, lits, eqs))
|
if (!m_graph.m_context_solver.upper_bound(e, up, lits, eqs))
|
||||||
return false;
|
return false;
|
||||||
for (auto lit : lits)
|
for (auto lit : lits)
|
||||||
dep = m_graph.dep_mgr().mk_join(dep, m_graph.dep_mgr().mk_leaf(lit));
|
dep = m_graph.dep_mgr().mk_join(dep, m_graph.dep_mgr().mk_leaf(lit));
|
||||||
|
|
@ -432,12 +432,13 @@ namespace seq {
|
||||||
// nielsen_graph
|
// nielsen_graph
|
||||||
// -----------------------------------------------
|
// -----------------------------------------------
|
||||||
|
|
||||||
nielsen_graph::nielsen_graph(euf::sgraph &sg, simple_solver &solver):
|
nielsen_graph::nielsen_graph(euf::sgraph &sg, sub_solver_i &solver, context_solver_i& ctx_solver):
|
||||||
m(sg.get_manager()),
|
m(sg.get_manager()),
|
||||||
a(sg.get_manager()),
|
a(sg.get_manager()),
|
||||||
m_seq(sg.get_seq_util()),
|
m_seq(sg.get_seq_util()),
|
||||||
m_sg(sg),
|
m_sg(sg),
|
||||||
m_solver(solver),
|
m_length_solver(solver),
|
||||||
|
m_context_solver(ctx_solver),
|
||||||
m_parikh(alloc(seq_parikh, sg)),
|
m_parikh(alloc(seq_parikh, sg)),
|
||||||
m_seq_regex(alloc(seq::seq_regex, sg)) {}
|
m_seq_regex(alloc(seq::seq_regex, sg)) {}
|
||||||
|
|
||||||
|
|
@ -550,7 +551,7 @@ namespace seq {
|
||||||
m_length_trail.reset();
|
m_length_trail.reset();
|
||||||
m_length_info.reset();
|
m_length_info.reset();
|
||||||
m_dep_mgr.reset();
|
m_dep_mgr.reset();
|
||||||
m_solver.reset();
|
m_length_solver.reset();
|
||||||
SASSERT(m_nodes.empty());
|
SASSERT(m_nodes.empty());
|
||||||
SASSERT(m_edges.empty());
|
SASSERT(m_edges.empty());
|
||||||
SASSERT(m_root == nullptr);
|
SASSERT(m_root == nullptr);
|
||||||
|
|
@ -1891,7 +1892,7 @@ namespace seq {
|
||||||
// constraints. The child's own new constraints will be asserted
|
// constraints. The child's own new constraints will be asserted
|
||||||
// inside the recursive call (above). On return, pop the scope so
|
// inside the recursive call (above). On return, pop the scope so
|
||||||
// that backtracking removes those assertions.
|
// that backtracking removes those assertions.
|
||||||
m_solver.push();
|
m_length_solver.push();
|
||||||
|
|
||||||
// Lazily compute substitution length constraints (|x| = |u|) on first
|
// Lazily compute substitution length constraints (|x| = |u|) on first
|
||||||
// traversal. This must happen before asserting side_constraints and before
|
// traversal. This must happen before asserting side_constraints and before
|
||||||
|
|
@ -1916,7 +1917,7 @@ namespace seq {
|
||||||
// Restore modification counts on backtrack.
|
// Restore modification counts on backtrack.
|
||||||
dec_edge_mod_counts(e);
|
dec_edge_mod_counts(e);
|
||||||
|
|
||||||
m_solver.pop(1);
|
m_length_solver.pop(1);
|
||||||
if (r == search_result::sat)
|
if (r == search_result::sat)
|
||||||
return search_result::sat;
|
return search_result::sat;
|
||||||
cur_path.pop_back();
|
cur_path.pop_back();
|
||||||
|
|
@ -4501,11 +4502,11 @@ namespace seq {
|
||||||
}
|
}
|
||||||
|
|
||||||
void nielsen_graph::assert_to_subsolver(const constraint& c) {
|
void nielsen_graph::assert_to_subsolver(const constraint& c) {
|
||||||
m_solver.assert_expr(c.fml, c.dep);
|
m_length_solver.assert_expr(c.fml, c.dep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nielsen_graph::assert_to_subsolver(expr* e) {
|
void nielsen_graph::assert_to_subsolver(expr* e) {
|
||||||
m_solver.assert_expr(e);
|
m_length_solver.assert_expr(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nielsen_graph::assert_node_new_int_constraints(nielsen_node* node) {
|
void nielsen_graph::assert_node_new_int_constraints(nielsen_node* node) {
|
||||||
|
|
@ -4514,10 +4515,9 @@ namespace seq {
|
||||||
// already present in the enclosing solver scope; asserting them again would
|
// already present in the enclosing solver scope; asserting them again would
|
||||||
// be redundant (though harmless). This is called by search_dfs right after
|
// be redundant (though harmless). This is called by search_dfs right after
|
||||||
// simplify_and_init, which is where new constraints are produced.
|
// simplify_and_init, which is where new constraints are produced.
|
||||||
SASSERT(m_literal_if_false);
|
|
||||||
for (unsigned i = node->m_parent_ic_count; i < node->constraints().size(); ++i) {
|
for (unsigned i = node->m_parent_ic_count; i < node->constraints().size(); ++i) {
|
||||||
auto& c = node->constraints()[i];
|
auto& c = node->constraints()[i];
|
||||||
auto lit = m_literal_if_false(c.fml);
|
auto lit = m_context_solver.literal_if_false(c.fml);
|
||||||
// std::cout << "Internalizing literal " << mk_pp(c.fml, m) << " [" << (lit == sat::null_literal) << "]" <<
|
// std::cout << "Internalizing literal " << mk_pp(c.fml, m) << " [" << (lit == sat::null_literal) << "]" <<
|
||||||
// std::endl;
|
// std::endl;
|
||||||
if (lit != sat::null_literal) {
|
if (lit != sat::null_literal) {
|
||||||
|
|
@ -4584,13 +4584,13 @@ namespace seq {
|
||||||
// (root length constraints at the base level, edge side_constraints and node
|
// (root length constraints at the base level, edge side_constraints and node
|
||||||
// constraints pushed/popped as the DFS descends and backtracks).
|
// constraints pushed/popped as the DFS descends and backtracks).
|
||||||
// A plain check() is therefore sufficient.
|
// A plain check() is therefore sufficient.
|
||||||
return m_solver.check() != l_false;
|
return m_length_solver.check() != l_false;
|
||||||
}
|
}
|
||||||
|
|
||||||
dep_tracker nielsen_graph::get_subsolver_dependency(nielsen_node* /*n*/) {
|
dep_tracker nielsen_graph::get_subsolver_dependency(nielsen_node* /*n*/) {
|
||||||
// check_int_feasibility() already called m_solver.check() which computed
|
// check_int_feasibility() already called m_solver.check() which computed
|
||||||
// the UNSAT core in terms of tracked assumption literals and their deps.
|
// the UNSAT core in terms of tracked assumption literals and their deps.
|
||||||
return m_solver.core();
|
return m_length_solver.core();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nielsen_graph::check_lp_le(expr* lhs, expr* rhs, nielsen_node* n, dep_tracker& dep) {
|
bool nielsen_graph::check_lp_le(expr* lhs, expr* rhs, nielsen_node* n, dep_tracker& dep) {
|
||||||
|
|
@ -4599,8 +4599,8 @@ namespace seq {
|
||||||
rational lhs_lo, rhs_up;
|
rational lhs_lo, rhs_up;
|
||||||
literal_vector lits;
|
literal_vector lits;
|
||||||
enode_pair_vector eqs;
|
enode_pair_vector eqs;
|
||||||
if (m_solver.lower_bound(lhs, lhs_lo, lits, eqs) &&
|
if (m_context_solver.lower_bound(lhs, lhs_lo, lits, eqs) &&
|
||||||
m_solver.upper_bound(rhs, rhs_up, lits, eqs) && lhs_lo > rhs_up)
|
m_context_solver.upper_bound(rhs, rhs_up, lits, eqs) && lhs_lo > rhs_up)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// lhs <= lhs_up <= rhs_lo <= rhs
|
// lhs <= lhs_up <= rhs_lo <= rhs
|
||||||
|
|
@ -4609,8 +4609,8 @@ namespace seq {
|
||||||
lits.reset();
|
lits.reset();
|
||||||
eqs.reset();
|
eqs.reset();
|
||||||
rational rhs_lo, lhs_up;
|
rational rhs_lo, lhs_up;
|
||||||
if (m_solver.upper_bound(lhs, lhs_up, lits, eqs) &&
|
if (m_context_solver.upper_bound(lhs, lhs_up, lits, eqs) &&
|
||||||
m_solver.lower_bound(rhs, rhs_lo, lits, eqs) &&
|
m_context_solver.lower_bound(rhs, rhs_lo, lits, eqs) &&
|
||||||
lhs_up <= rhs_lo) {
|
lhs_up <= rhs_lo) {
|
||||||
for (auto lit : lits)
|
for (auto lit : lits)
|
||||||
dep = m_dep_mgr.mk_join(dep, m_dep_mgr.mk_leaf(lit));
|
dep = m_dep_mgr.mk_join(dep, m_dep_mgr.mk_leaf(lit));
|
||||||
|
|
@ -4629,12 +4629,12 @@ namespace seq {
|
||||||
expr_ref one(a.mk_int(1), m);
|
expr_ref one(a.mk_int(1), m);
|
||||||
expr_ref rhs_plus_one(a.mk_add(rhs, one), m);
|
expr_ref rhs_plus_one(a.mk_add(rhs, one), m);
|
||||||
|
|
||||||
m_solver.push();
|
m_length_solver.push();
|
||||||
assert_to_subsolver(a.mk_ge(lhs, rhs_plus_one));
|
assert_to_subsolver(a.mk_ge(lhs, rhs_plus_one));
|
||||||
lbool result = m_solver.check();
|
lbool result = m_length_solver.check();
|
||||||
if (result == l_false)
|
if (result == l_false)
|
||||||
dep = m_solver.core();
|
dep = m_length_solver.core();
|
||||||
m_solver.pop(1);
|
m_length_solver.pop(1);
|
||||||
if (result == l_false) {
|
if (result == l_false) {
|
||||||
n->add_constraint(constraint(a.mk_le(lhs, rhs), dep, m));
|
n->add_constraint(constraint(a.mk_le(lhs, rhs), dep, m));
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,6 @@ namespace seq {
|
||||||
using enode_pair = std::pair<smt::enode *, smt::enode *>;
|
using enode_pair = std::pair<smt::enode *, smt::enode *>;
|
||||||
using literal_vector = svector<sat::literal>;
|
using literal_vector = svector<sat::literal>;
|
||||||
using enode_pair_vector = svector<enode_pair>;
|
using enode_pair_vector = svector<enode_pair>;
|
||||||
|
|
||||||
using dep_source = std::variant<sat::literal, enode_pair>;
|
using dep_source = std::variant<sat::literal, enode_pair>;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -338,21 +337,39 @@ namespace seq {
|
||||||
* to serve as the arithmetic back-end. When no solver is provided,
|
* to serve as the arithmetic back-end. When no solver is provided,
|
||||||
* integer feasibility checks are skipped (optimistically assumed feasible).
|
* integer feasibility checks are skipped (optimistically assumed feasible).
|
||||||
*/
|
*/
|
||||||
class simple_solver {
|
class sub_solver_i {
|
||||||
public:
|
public:
|
||||||
virtual ~simple_solver() {}
|
virtual ~sub_solver_i() {}
|
||||||
virtual lbool check() = 0;
|
virtual lbool check() = 0;
|
||||||
virtual void assert_expr(expr* e, dep_tracker dep = nullptr) = 0;
|
virtual void assert_expr(expr* e, dep_tracker dep = nullptr) = 0;
|
||||||
virtual void push() = 0;
|
virtual void push() = 0;
|
||||||
virtual void pop(unsigned num_scopes) = 0;
|
virtual void pop(unsigned num_scopes) = 0;
|
||||||
virtual void get_model(model_ref& mdl) { mdl = nullptr; }
|
virtual void get_model(model_ref& mdl) { mdl = nullptr; }
|
||||||
virtual dep_tracker core() { return nullptr; }
|
virtual dep_tracker core() { return nullptr; }
|
||||||
|
virtual void reset() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class context_solver_i {
|
||||||
|
public:
|
||||||
|
virtual ~context_solver_i() {}
|
||||||
|
|
||||||
|
bool m_should_internalize = false;
|
||||||
|
|
||||||
// Optional bound queries on arithmetic expressions (non-strict integer bounds).
|
// Optional bound queries on arithmetic expressions (non-strict integer bounds).
|
||||||
// Default implementation reports "unsupported".
|
// Default implementation reports "unsupported".
|
||||||
virtual bool lower_bound(expr* e, rational& l, literal_vector& lits, enode_pair_vector& eqs) const { return false; }
|
virtual bool lower_bound(expr *e, rational &l, literal_vector &lits, enode_pair_vector &eqs) const {
|
||||||
virtual bool upper_bound(expr* e, rational& hi, literal_vector& lits, enode_pair_vector& eqs) const { return false; }
|
return false;
|
||||||
virtual bool current_value(expr* e, rational& v) const { return false; }
|
}
|
||||||
virtual void reset() = 0;
|
virtual bool upper_bound(expr *e, rational &hi, literal_vector &lits, enode_pair_vector &eqs) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual bool current_value(expr *e, rational &v) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual sat::literal literal_if_false(expr* e) {
|
||||||
|
return sat::null_literal;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// partition dep_source leaves from deps into enode pairs, sat literals,
|
// partition dep_source leaves from deps into enode pairs, sat literals,
|
||||||
|
|
@ -840,10 +857,13 @@ namespace seq {
|
||||||
|
|
||||||
// -----------------------------------------------
|
// -----------------------------------------------
|
||||||
// Integer subsolver (abstract interface)
|
// Integer subsolver (abstract interface)
|
||||||
// When m_solver is null, check_int_feasibility skips arithmetic checking
|
|
||||||
// and optimistically assumes feasibility.
|
|
||||||
// -----------------------------------------------
|
// -----------------------------------------------
|
||||||
simple_solver& m_solver;
|
sub_solver_i& m_length_solver;
|
||||||
|
|
||||||
|
// -----------------------------------------------
|
||||||
|
// Interface to solver context
|
||||||
|
// -----------------------------------------------
|
||||||
|
context_solver_i &m_context_solver;
|
||||||
|
|
||||||
// Constraint.Shared: guards re-assertion of root-level constraints.
|
// Constraint.Shared: guards re-assertion of root-level constraints.
|
||||||
// Set to true after assert_root_constraints_to_solver() is first called.
|
// Set to true after assert_root_constraints_to_solver() is first called.
|
||||||
|
|
@ -857,9 +877,6 @@ namespace seq {
|
||||||
// inclusion, derivatives. Allocated in the constructor; owned by this graph.
|
// inclusion, derivatives. Allocated in the constructor; owned by this graph.
|
||||||
seq::seq_regex* m_seq_regex = nullptr;
|
seq::seq_regex* m_seq_regex = nullptr;
|
||||||
|
|
||||||
// Callback to check that literals assumed in branches are not already assigned to false.
|
|
||||||
// returns the literal that is assigned to false, otherwise returns a null_literal
|
|
||||||
std::function<sat::literal(expr *)> m_literal_if_false;
|
|
||||||
|
|
||||||
// Maps each variable to its current length term
|
// Maps each variable to its current length term
|
||||||
|
|
||||||
|
|
@ -886,12 +903,9 @@ namespace seq {
|
||||||
public:
|
public:
|
||||||
// Construct with a caller-supplied solver. Ownership is NOT transferred;
|
// Construct with a caller-supplied solver. Ownership is NOT transferred;
|
||||||
// the caller is responsible for keeping the solver alive.
|
// the caller is responsible for keeping the solver alive.
|
||||||
nielsen_graph(euf::sgraph& sg, simple_solver& solver);
|
nielsen_graph(euf::sgraph& sg, sub_solver_i& solver, context_solver_i& ctx);
|
||||||
~nielsen_graph();
|
~nielsen_graph();
|
||||||
|
|
||||||
void set_literal_if_false(std::function<sat::literal(expr* e)> const& lif) {
|
|
||||||
m_literal_if_false = lif;
|
|
||||||
}
|
|
||||||
|
|
||||||
ast_manager &get_manager() {
|
ast_manager &get_manager() {
|
||||||
return m;
|
return m;
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,9 @@ namespace smt {
|
||||||
m_arith_value(m),
|
m_arith_value(m),
|
||||||
m_egraph(m),
|
m_egraph(m),
|
||||||
m_sgraph(m, m_egraph),
|
m_sgraph(m, m_egraph),
|
||||||
m_context_solver(m),
|
m_length_solver(m),
|
||||||
m_nielsen(m_sgraph, m_context_solver),
|
m_context_solver(ctx),
|
||||||
|
m_nielsen(m_sgraph, m_length_solver, m_context_solver),
|
||||||
m_axioms(m_th_rewriter),
|
m_axioms(m_th_rewriter),
|
||||||
m_regex(m_sgraph),
|
m_regex(m_sgraph),
|
||||||
m_model(m, ctx, m_seq, m_rewriter, m_sgraph),
|
m_model(m, ctx, m_seq, m_rewriter, m_sgraph),
|
||||||
|
|
@ -82,34 +83,8 @@ namespace smt {
|
||||||
m_axioms.set_ensure_digits(ensure_digit_axiom);
|
m_axioms.set_ensure_digits(ensure_digit_axiom);
|
||||||
m_axioms.set_mark_no_diseq(mark_no_diseq);
|
m_axioms.set_mark_no_diseq(mark_no_diseq);
|
||||||
|
|
||||||
m_should_internalize = true; // delete this if using internalize as fallback
|
m_context_solver.m_should_internalize = true; // delete this if using internalize as fallback
|
||||||
std::function<sat::literal(expr*)> literal_if_false = [&](expr* e) {
|
|
||||||
bool is_not = m.is_not(e, e);
|
|
||||||
if (m_should_internalize && !ctx.b_internalized(e)) {
|
|
||||||
// it can happen that the element is not internalized, but as soon as we do it, it becomes false.
|
|
||||||
// In case we just skip one of those uninternalized expressions,
|
|
||||||
// adding the Nielsen assumption later will fail
|
|
||||||
// Alternatively, we could just retry Nielsen saturation in case
|
|
||||||
// adding the Nielsen assumption yields the assumption being false after internalizing
|
|
||||||
ctx.internalize(to_app(e), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ctx.b_internalized(e))
|
|
||||||
return sat::null_literal;
|
|
||||||
|
|
||||||
literal lit = ctx.get_literal(e);
|
|
||||||
if (is_not)
|
|
||||||
lit.neg();
|
|
||||||
if (ctx.get_assignment(lit) == l_false) {
|
|
||||||
// TRACE(seq, tout << "literal_if_false: " << lit << " " << mk_pp(e, m) << " is assigned false\n");
|
|
||||||
return lit;
|
|
||||||
}
|
|
||||||
// TRACE(seq, tout << "literal_if_false: " << mk_pp(e, m) << " is assigned " << ctx.get_assignment(lit) << "\n");
|
|
||||||
return sat::null_literal;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
m_nielsen.set_literal_if_false(literal_if_false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
@ -848,12 +823,11 @@ namespace smt {
|
||||||
break;
|
break;
|
||||||
case l_false:
|
case l_false:
|
||||||
// this should not happen because nielsen checks for this before returning a satisfying path.
|
// this should not happen because nielsen checks for this before returning a satisfying path.
|
||||||
// or maybe it can happen if we have a "le" dependency
|
|
||||||
TRACE(seq, tout << "nseq final_check: nielsen assumption " << c.fml << " is false; internalized - " << ctx.e_internalized(c.fml) << "\n");
|
TRACE(seq, tout << "nseq final_check: nielsen assumption " << c.fml << " is false; internalized - " << ctx.e_internalized(c.fml) << "\n");
|
||||||
all_sat = false;
|
all_sat = false;
|
||||||
// std::cout << "False [" << lit << "]: " << mk_pp(c.fml, m) << std::endl;
|
// std::cout << "False [" << lit << "]: " << mk_pp(c.fml, m) << std::endl;
|
||||||
ctx.push_trail(value_trail(m_should_internalize));
|
ctx.push_trail(value_trail(m_context_solver.m_should_internalize));
|
||||||
m_should_internalize = true;
|
m_context_solver.m_should_internalize = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// use assumptions to bound search
|
// use assumptions to bound search
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,12 @@ namespace smt {
|
||||||
euf::sgraph m_sgraph; // private sgraph
|
euf::sgraph m_sgraph; // private sgraph
|
||||||
// m_context_solver must be declared before m_nielsen: its address is passed
|
// 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.
|
// to the m_nielsen constructor and must remain stable for the object's lifetime.
|
||||||
|
sub_solver m_length_solver;
|
||||||
context_solver m_context_solver;
|
context_solver m_context_solver;
|
||||||
seq::nielsen_graph m_nielsen;
|
seq::nielsen_graph m_nielsen;
|
||||||
seq::axioms m_axioms;
|
seq::axioms m_axioms;
|
||||||
seq::seq_regex m_regex; // regex membership pre-processing
|
seq::seq_regex m_regex; // regex membership pre-processing
|
||||||
seq_model m_model; // model construction helper
|
seq_model m_model; // model construction helper
|
||||||
bool m_should_internalize = false;
|
|
||||||
|
|
||||||
// propagation queue items (variant over the distinct propagation cases)
|
// propagation queue items (variant over the distinct propagation cases)
|
||||||
using eq_item = tracked_str_eq; // string equality
|
using eq_item = tracked_str_eq; // string equality
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ Abstract:
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
// Trivial solver that always returns sat and ignores all assertions.
|
// Trivial solver that always returns sat and ignores all assertions.
|
||||||
class nseq_basic_dummy_solver : public seq::simple_solver {
|
class nseq_basic_dummy_solver : public seq::sub_solver_i {
|
||||||
public:
|
public:
|
||||||
void push() override {}
|
void push() override {}
|
||||||
void pop(unsigned) override {}
|
void pop(unsigned) override {}
|
||||||
|
|
@ -40,7 +40,8 @@ static void test_nseq_instantiation() {
|
||||||
euf::egraph eg(m);
|
euf::egraph eg(m);
|
||||||
euf::sgraph sg(m, eg);
|
euf::sgraph sg(m, eg);
|
||||||
nseq_basic_dummy_solver solver;
|
nseq_basic_dummy_solver solver;
|
||||||
seq::nielsen_graph ng(sg, solver);
|
seq::context_solver_i context_solver;
|
||||||
|
seq::nielsen_graph ng(sg, solver, context_solver);
|
||||||
SASSERT(ng.root() == nullptr);
|
SASSERT(ng.root() == nullptr);
|
||||||
SASSERT(ng.num_nodes() == 0);
|
SASSERT(ng.num_nodes() == 0);
|
||||||
std::cout << " ok\n";
|
std::cout << " ok\n";
|
||||||
|
|
@ -97,7 +98,8 @@ static void test_nseq_simplification() {
|
||||||
euf::egraph eg(m);
|
euf::egraph eg(m);
|
||||||
euf::sgraph sg(m, eg);
|
euf::sgraph sg(m, eg);
|
||||||
nseq_basic_dummy_solver solver;
|
nseq_basic_dummy_solver solver;
|
||||||
seq::nielsen_graph ng(sg, solver);
|
seq::context_solver_i context_solver;
|
||||||
|
seq::nielsen_graph ng(sg, solver, context_solver);
|
||||||
|
|
||||||
// Add a trivial equality: empty = empty
|
// Add a trivial equality: empty = empty
|
||||||
euf::snode* empty1 = sg.mk_empty_seq(su.str.mk_string_sort());
|
euf::snode* empty1 = sg.mk_empty_seq(su.str.mk_string_sort());
|
||||||
|
|
@ -120,7 +122,8 @@ static void test_nseq_node_satisfied() {
|
||||||
euf::egraph eg(m);
|
euf::egraph eg(m);
|
||||||
euf::sgraph sg(m, eg);
|
euf::sgraph sg(m, eg);
|
||||||
nseq_basic_dummy_solver solver;
|
nseq_basic_dummy_solver solver;
|
||||||
seq::nielsen_graph ng(sg, solver);
|
seq::context_solver_i context_solver;
|
||||||
|
seq::nielsen_graph ng(sg, solver, context_solver);
|
||||||
|
|
||||||
seq::nielsen_node *node = ng.mk_node();
|
seq::nielsen_node *node = ng.mk_node();
|
||||||
// empty node has no constraints => satisfied
|
// empty node has no constraints => satisfied
|
||||||
|
|
@ -149,7 +152,8 @@ static void test_nseq_symbol_clash() {
|
||||||
euf::egraph eg(m);
|
euf::egraph eg(m);
|
||||||
euf::sgraph sg(m, eg);
|
euf::sgraph sg(m, eg);
|
||||||
nseq_basic_dummy_solver solver;
|
nseq_basic_dummy_solver solver;
|
||||||
seq::nielsen_graph ng(sg, solver);
|
seq::context_solver_i context_solver;
|
||||||
|
seq::nielsen_graph ng(sg, solver, context_solver);
|
||||||
|
|
||||||
euf::snode* a = sg.mk_char('a');
|
euf::snode* a = sg.mk_char('a');
|
||||||
euf::snode* b = sg.mk_char('b');
|
euf::snode* b = sg.mk_char('b');
|
||||||
|
|
@ -176,7 +180,8 @@ static void test_nseq_var_eq_self() {
|
||||||
euf::egraph eg(m);
|
euf::egraph eg(m);
|
||||||
euf::sgraph sg(m, eg);
|
euf::sgraph sg(m, eg);
|
||||||
nseq_basic_dummy_solver solver;
|
nseq_basic_dummy_solver solver;
|
||||||
seq::nielsen_graph ng(sg, solver);
|
seq::context_solver_i context_solver;
|
||||||
|
seq::nielsen_graph ng(sg, solver, context_solver);
|
||||||
|
|
||||||
euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort());
|
euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort());
|
||||||
ng.add_str_eq(x, x);
|
ng.add_str_eq(x, x);
|
||||||
|
|
@ -194,7 +199,8 @@ static void test_nseq_prefix_clash() {
|
||||||
euf::egraph eg(m);
|
euf::egraph eg(m);
|
||||||
euf::sgraph sg(m, eg);
|
euf::sgraph sg(m, eg);
|
||||||
nseq_basic_dummy_solver solver;
|
nseq_basic_dummy_solver solver;
|
||||||
seq::nielsen_graph ng(sg, solver);
|
seq::context_solver_i context_solver;
|
||||||
|
seq::nielsen_graph ng(sg, solver, context_solver);
|
||||||
|
|
||||||
euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort());
|
euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort());
|
||||||
euf::snode* a = sg.mk_char('a');
|
euf::snode* a = sg.mk_char('a');
|
||||||
|
|
@ -216,7 +222,8 @@ static void test_nseq_const_nielsen_solvable() {
|
||||||
euf::egraph eg(m);
|
euf::egraph eg(m);
|
||||||
euf::sgraph sg(m, eg);
|
euf::sgraph sg(m, eg);
|
||||||
nseq_basic_dummy_solver solver;
|
nseq_basic_dummy_solver solver;
|
||||||
seq::nielsen_graph ng(sg, solver);
|
seq::context_solver_i context_solver;
|
||||||
|
seq::nielsen_graph ng(sg, solver, context_solver);
|
||||||
|
|
||||||
euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort());
|
euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort());
|
||||||
euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort());
|
euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort());
|
||||||
|
|
@ -239,8 +246,8 @@ static void test_nseq_length_mismatch() {
|
||||||
euf::egraph eg(m);
|
euf::egraph eg(m);
|
||||||
euf::sgraph sg(m, eg);
|
euf::sgraph sg(m, eg);
|
||||||
nseq_basic_dummy_solver solver;
|
nseq_basic_dummy_solver solver;
|
||||||
seq::nielsen_graph ng(sg, solver);
|
seq::context_solver_i context_solver;
|
||||||
|
seq::nielsen_graph ng(sg, solver, context_solver);
|
||||||
euf::snode* a = sg.mk_char('a');
|
euf::snode* a = sg.mk_char('a');
|
||||||
euf::snode* b = sg.mk_char('b');
|
euf::snode* b = sg.mk_char('b');
|
||||||
euf::snode* ab = sg.mk_concat(a, b);
|
euf::snode* ab = sg.mk_concat(a, b);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ Abstract:
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
// Trivial solver that always returns sat and ignores all assertions.
|
// Trivial solver that always returns sat and ignores all assertions.
|
||||||
class nseq_zipt_dummy_solver : public seq::simple_solver {
|
class nseq_zipt_dummy_solver : public seq::sub_solver_i {
|
||||||
public:
|
public:
|
||||||
void push() override {}
|
void push() override {}
|
||||||
void pop(unsigned) override {}
|
void pop(unsigned) override {}
|
||||||
|
|
@ -43,7 +43,7 @@ public:
|
||||||
// Trivial simple_solver stub: optimistically assumes integer constraints
|
// Trivial simple_solver stub: optimistically assumes integer constraints
|
||||||
// are always feasible (returns l_true without actually checking).
|
// are always feasible (returns l_true without actually checking).
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
class zipt_dummy_simple_solver : public seq::simple_solver {
|
class zipt_dummy_simple_solver : public seq::sub_solver_i {
|
||||||
public:
|
public:
|
||||||
void push() override {}
|
void push() override {}
|
||||||
void pop(unsigned) override {}
|
void pop(unsigned) override {}
|
||||||
|
|
@ -184,6 +184,7 @@ struct nseq_fixture {
|
||||||
euf::egraph eg;
|
euf::egraph eg;
|
||||||
euf::sgraph sg;
|
euf::sgraph sg;
|
||||||
zipt_dummy_simple_solver dummy_solver;
|
zipt_dummy_simple_solver dummy_solver;
|
||||||
|
seq::context_solver_i context_solver;
|
||||||
seq::nielsen_graph ng;
|
seq::nielsen_graph ng;
|
||||||
seq_util su;
|
seq_util su;
|
||||||
str_builder sb;
|
str_builder sb;
|
||||||
|
|
@ -193,7 +194,7 @@ struct nseq_fixture {
|
||||||
static ast_manager& init(ast_manager& m) { reg_decl_plugins(m); return m; }
|
static ast_manager& init(ast_manager& m) { reg_decl_plugins(m); return m; }
|
||||||
|
|
||||||
nseq_fixture()
|
nseq_fixture()
|
||||||
: eg(init(m)), sg(m, eg), ng(sg, dummy_solver), su(m), sb(sg, su), rb(m, su, sg)
|
: eg(init(m)), sg(m, eg), dummy_solver(), context_solver(), ng(sg, dummy_solver, context_solver), su(m), sb(sg, su), rb(m, su, sg)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
euf::snode* S(const char* s) { return sb.parse(s); }
|
euf::snode* S(const char* s) { return sb.parse(s); }
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -38,7 +38,7 @@ Author:
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Minimal solver stub (no-op)
|
// Minimal solver stub (no-op)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
class parikh_test_solver : public seq::simple_solver {
|
class parikh_test_solver : public seq::sub_solver_i {
|
||||||
public:
|
public:
|
||||||
void push() override {}
|
void push() override {}
|
||||||
void pop(unsigned) override {}
|
void pop(unsigned) override {}
|
||||||
|
|
@ -486,7 +486,8 @@ static void test_apply_to_node_adds_constraints() {
|
||||||
euf::sgraph sg(m, eg);
|
euf::sgraph sg(m, eg);
|
||||||
seq_util seq(m);
|
seq_util seq(m);
|
||||||
parikh_test_solver solver;
|
parikh_test_solver solver;
|
||||||
seq::nielsen_graph ng(sg, solver);
|
seq::context_solver_i context_solver;
|
||||||
|
seq::nielsen_graph ng(sg, solver, context_solver);
|
||||||
seq::seq_parikh parikh(sg);
|
seq::seq_parikh parikh(sg);
|
||||||
|
|
||||||
euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort());
|
euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort());
|
||||||
|
|
@ -518,7 +519,8 @@ static void test_apply_to_node_stride_one_no_constraints() {
|
||||||
seq_util seq(m);
|
seq_util seq(m);
|
||||||
sort_ref str_sort(seq.str.mk_string_sort(), m);
|
sort_ref str_sort(seq.str.mk_string_sort(), m);
|
||||||
parikh_test_solver solver;
|
parikh_test_solver solver;
|
||||||
seq::nielsen_graph ng(sg, solver);
|
seq::context_solver_i context_solver;
|
||||||
|
seq::nielsen_graph ng(sg, solver, context_solver);
|
||||||
seq::seq_parikh parikh(sg);
|
seq::seq_parikh parikh(sg);
|
||||||
|
|
||||||
euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort());
|
euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort());
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue