3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-05-25 19:36:20 +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:
Nikolaj Bjorner 2026-05-17 19:01:03 -07:00
parent 9d4feed0ae
commit 2a36b9a68e
9 changed files with 441 additions and 240 deletions

View file

@ -284,7 +284,7 @@ namespace seq {
enode_pair_vector eqs;
if (m_graph.a.is_numeral(e, lo))
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;
for (auto lit : lits)
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;
if (m_graph.a.is_numeral(e, up))
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;
for (auto lit : lits)
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(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()),
a(sg.get_manager()),
m_seq(sg.get_seq_util()),
m_sg(sg),
m_solver(solver),
m_length_solver(solver),
m_context_solver(ctx_solver),
m_parikh(alloc(seq_parikh, sg)),
m_seq_regex(alloc(seq::seq_regex, sg)) {}
@ -550,7 +551,7 @@ namespace seq {
m_length_trail.reset();
m_length_info.reset();
m_dep_mgr.reset();
m_solver.reset();
m_length_solver.reset();
SASSERT(m_nodes.empty());
SASSERT(m_edges.empty());
SASSERT(m_root == nullptr);
@ -1891,7 +1892,7 @@ namespace seq {
// constraints. The child's own new constraints will be asserted
// inside the recursive call (above). On return, pop the scope so
// that backtracking removes those assertions.
m_solver.push();
m_length_solver.push();
// Lazily compute substitution length constraints (|x| = |u|) on first
// traversal. This must happen before asserting side_constraints and before
@ -1916,7 +1917,7 @@ namespace seq {
// Restore modification counts on backtrack.
dec_edge_mod_counts(e);
m_solver.pop(1);
m_length_solver.pop(1);
if (r == search_result::sat)
return search_result::sat;
cur_path.pop_back();
@ -4501,11 +4502,11 @@ namespace seq {
}
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) {
m_solver.assert_expr(e);
m_length_solver.assert_expr(e);
}
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
// be redundant (though harmless). This is called by search_dfs right after
// 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) {
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::endl;
if (lit != sat::null_literal) {
@ -4584,13 +4584,13 @@ namespace seq {
// (root length constraints at the base level, edge side_constraints and node
// constraints pushed/popped as the DFS descends and backtracks).
// 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*/) {
// check_int_feasibility() already called m_solver.check() which computed
// 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) {
@ -4599,8 +4599,8 @@ namespace seq {
rational lhs_lo, rhs_up;
literal_vector lits;
enode_pair_vector eqs;
if (m_solver.lower_bound(lhs, lhs_lo, lits, eqs) &&
m_solver.upper_bound(rhs, rhs_up, lits, eqs) && lhs_lo > rhs_up)
if (m_context_solver.lower_bound(lhs, lhs_lo, lits, eqs) &&
m_context_solver.upper_bound(rhs, rhs_up, lits, eqs) && lhs_lo > rhs_up)
return false;
// lhs <= lhs_up <= rhs_lo <= rhs
@ -4609,8 +4609,8 @@ namespace seq {
lits.reset();
eqs.reset();
rational rhs_lo, lhs_up;
if (m_solver.upper_bound(lhs, lhs_up, lits, eqs) &&
m_solver.lower_bound(rhs, rhs_lo, lits, eqs) &&
if (m_context_solver.upper_bound(lhs, lhs_up, lits, eqs) &&
m_context_solver.lower_bound(rhs, rhs_lo, lits, eqs) &&
lhs_up <= rhs_lo) {
for (auto lit : lits)
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 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));
lbool result = m_solver.check();
lbool result = m_length_solver.check();
if (result == l_false)
dep = m_solver.core();
m_solver.pop(1);
dep = m_length_solver.core();
m_length_solver.pop(1);
if (result == l_false) {
n->add_constraint(constraint(a.mk_le(lhs, rhs), dep, m));
return true;