mirror of
https://github.com/Z3Prover/z3
synced 2026-04-15 08:44:10 +00:00
We should not stop eagerly on local conflicts
This commit is contained in:
parent
857e93fdb2
commit
684f93bed4
2 changed files with 62 additions and 49 deletions
|
|
@ -247,29 +247,27 @@ namespace seq {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool nielsen_node::add_constraint(constraint const &c) {
|
||||
void nielsen_node::add_constraint(constraint const &c) {
|
||||
auto& m = graph().get_manager();
|
||||
if (m.is_and(c.fml)) {
|
||||
// We have to add all - even if some of it conflict
|
||||
// [otw. we would leave the node partially initialized]
|
||||
for (const auto f : *to_app(c.fml)) {
|
||||
if (!add_constraint(constraint(f, c.dep, m)))
|
||||
return false;
|
||||
add_constraint(constraint(f, c.dep, m));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
expr* l, *r;
|
||||
if (m.is_eq(c.fml, l, r)) {
|
||||
if (l == r)
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
m_constraints.push_back(c);
|
||||
if (m_graph.m_literal_if_false) {
|
||||
auto lit = m_graph.m_literal_if_false(c.fml);
|
||||
if (lit != sat::null_literal) {
|
||||
set_external_conflict(lit, c.dep);
|
||||
return false;
|
||||
}
|
||||
SASSERT(m_graph.m_literal_if_false);
|
||||
auto lit = m_graph.m_literal_if_false(c.fml);
|
||||
if (lit != sat::null_literal) {
|
||||
std::cout << "External literal " << lit << " in node " << m_id << std::endl;
|
||||
set_external_conflict(lit, c.dep);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void nielsen_node::apply_subst(euf::sgraph& sg, nielsen_subst const& s) {
|
||||
|
|
@ -310,7 +308,7 @@ namespace seq {
|
|||
if (m_char_ranges.contains(var_id)) {
|
||||
auto range = m_char_ranges.find(var_id); // copy exactly
|
||||
m_char_ranges.remove(var_id);
|
||||
add_char_range(s.m_replacement, range.first, range.second);
|
||||
add_char_range(s.m_replacement, range.first, m_graph.dep_mgr().mk_join(range.second, s.m_dep));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -326,7 +324,7 @@ namespace seq {
|
|||
return; // matches, no conflict
|
||||
}
|
||||
set_conflict(backtrack_reason::character_range, dep);
|
||||
set_general_conflict(true);
|
||||
set_general_conflict();
|
||||
return;
|
||||
}
|
||||
SASSERT(sym_char && sym_char->is_unit());
|
||||
|
|
@ -337,7 +335,7 @@ namespace seq {
|
|||
existing = std::make_pair(inter, graph().dep_mgr().mk_join(existing.second, dep));
|
||||
if (inter.is_empty()) {
|
||||
set_conflict(backtrack_reason::character_range, existing.second);
|
||||
set_general_conflict(true);
|
||||
set_general_conflict();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -345,12 +343,14 @@ namespace seq {
|
|||
}
|
||||
|
||||
bool nielsen_node::lower_bound(expr* e, rational& lo) const {
|
||||
// TODO: Return dependencies
|
||||
SASSERT(e);
|
||||
return m_graph.m_solver.lower_bound(e, lo);
|
||||
}
|
||||
|
||||
|
||||
bool nielsen_node::upper_bound(expr* e, rational& up) const {
|
||||
// TODO: Return dependencies
|
||||
SASSERT(e);
|
||||
rational v;
|
||||
if (m_graph.a.is_numeral(e, v)) {
|
||||
|
|
@ -490,7 +490,7 @@ namespace seq {
|
|||
return t->is_var() || t->is_power();
|
||||
});
|
||||
if (has_char || !all_eliminable) {
|
||||
set_general_conflict(true);
|
||||
set_general_conflict();
|
||||
set_conflict(backtrack_reason::symbol_clash, dep);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -848,7 +848,7 @@ namespace seq {
|
|||
++prefix;
|
||||
}
|
||||
else if (sg.are_unit_distinct(lt, rt)) {
|
||||
m_is_general_conflict = true;
|
||||
set_general_conflict();
|
||||
set_conflict(backtrack_reason::symbol_clash, eq.m_dep);
|
||||
return simplify_result::conflict;
|
||||
}
|
||||
|
|
@ -865,7 +865,7 @@ namespace seq {
|
|||
if (m.are_equal(lt->get_expr(), rt->get_expr())) {
|
||||
++suffix;
|
||||
} else if (sg.are_unit_distinct(lt, rt)) {
|
||||
m_is_general_conflict = true;
|
||||
set_general_conflict();
|
||||
set_conflict(backtrack_reason::symbol_clash, eq.m_dep);
|
||||
return simplify_result::conflict;
|
||||
}
|
||||
|
|
@ -884,7 +884,8 @@ namespace seq {
|
|||
// pass 3: power simplification (mirrors ZIPT's LcpCompression +
|
||||
// SimplifyPowerElim + SimplifyPowerSingle)
|
||||
for (str_eq& eq : m_str_eq) {
|
||||
if (eq.is_trivial() || !eq.m_lhs || !eq.m_rhs)
|
||||
SASSERT(eq.m_lhs && eq.m_rhs);
|
||||
if (eq.is_trivial())
|
||||
continue;
|
||||
|
||||
// 3a: simplify constant-exponent powers (base^0 → ε, base^1 → base)
|
||||
|
|
@ -1065,7 +1066,7 @@ namespace seq {
|
|||
if (!deriv)
|
||||
break;
|
||||
if (deriv->is_fail()) {
|
||||
m_is_general_conflict = true;
|
||||
set_general_conflict();
|
||||
set_conflict(backtrack_reason::regex, mem.m_dep);
|
||||
return simplify_result::conflict;
|
||||
}
|
||||
|
|
@ -1105,7 +1106,7 @@ namespace seq {
|
|||
|
||||
auto next = sg.mk(d);
|
||||
if (next->is_fail()) {
|
||||
m_is_general_conflict = true;
|
||||
set_general_conflict();
|
||||
set_conflict(backtrack_reason::regex, mem.m_dep);
|
||||
return simplify_result::conflict;
|
||||
}
|
||||
|
|
@ -1119,7 +1120,7 @@ namespace seq {
|
|||
// check for regex memberships that are immediately infeasible
|
||||
for (str_mem& mem : m_str_mem) {
|
||||
if (mem.is_contradiction()) {
|
||||
m_is_general_conflict = true;
|
||||
set_general_conflict();
|
||||
set_conflict(backtrack_reason::regex, mem.m_dep);
|
||||
return simplify_result::conflict;
|
||||
}
|
||||
|
|
@ -1137,7 +1138,7 @@ namespace seq {
|
|||
continue;
|
||||
dep_tracker dep = mem.m_dep;
|
||||
if (m_graph.check_regex_widening(*this, mem.m_str, mem.m_regex, dep)) {
|
||||
m_is_general_conflict = true;
|
||||
set_general_conflict();
|
||||
set_conflict(backtrack_reason::regex, dep);
|
||||
return simplify_result::conflict;
|
||||
}
|
||||
|
|
@ -1179,7 +1180,7 @@ namespace seq {
|
|||
// node as a Parikh-image conflict immediately (avoids a solver call).
|
||||
str_mem const* confl_cnstr;
|
||||
if (!node.is_currently_conflict() && (confl_cnstr = m_parikh->check_parikh_conflict(node)) != nullptr) {
|
||||
node.set_general_conflict(true);
|
||||
node.set_general_conflict();
|
||||
node.set_conflict(backtrack_reason::parikh_image, confl_cnstr->m_dep);
|
||||
}
|
||||
}
|
||||
|
|
@ -1311,7 +1312,8 @@ namespace seq {
|
|||
|
||||
// revisit detection: if already visited this run (e.g., iterative deepening), return cached status.
|
||||
// mirrors ZIPT's NielsenNode.GraphExpansion() evalIdx check.
|
||||
if (node->eval_idx() == m_run_idx) {
|
||||
unsigned eval_idx = node->eval_idx();
|
||||
if (eval_idx == m_run_idx) {
|
||||
if (node->is_satisfied()) {
|
||||
m_sat_node = node;
|
||||
m_sat_path = cur_path;
|
||||
|
|
@ -1323,28 +1325,22 @@ namespace seq {
|
|||
}
|
||||
node->set_eval_idx(m_run_idx);
|
||||
SASSERT(!node->is_general_conflict());
|
||||
node->clear_local_conflict(); // clear local conflicts from previous runs
|
||||
if (eval_idx > 0)
|
||||
// adding the side-conditions from the edge
|
||||
// when generating this node might trigger a local conflict
|
||||
node->clear_local_conflict(); // clear local conflicts from previous runs
|
||||
|
||||
// we might need to tell the SAT solver about the new integer inequalities
|
||||
// that might have been added by an extension step
|
||||
assert_node_new_int_constraints(node);
|
||||
|
||||
if (node->is_currently_conflict()) {
|
||||
++m_stats.m_num_simplify_conflict;
|
||||
return search_result::unsat;
|
||||
}
|
||||
|
||||
// simplify constraints (idempotent after first call)
|
||||
const simplify_result sr = node->simplify_and_init(cur_path);
|
||||
|
||||
if (sr == simplify_result::conflict) {
|
||||
++m_stats.m_num_simplify_conflict;
|
||||
node->set_general_conflict(true);
|
||||
return search_result::unsat;
|
||||
}
|
||||
|
||||
if (node->is_currently_conflict()) {
|
||||
if (sr == simplify_result::conflict || node->is_general_conflict()) {
|
||||
SASSERT(node->is_general_conflict());
|
||||
++m_stats.m_num_simplify_conflict;
|
||||
node->set_general_conflict();
|
||||
return search_result::unsat;
|
||||
}
|
||||
|
||||
|
|
@ -1354,7 +1350,8 @@ namespace seq {
|
|||
// Note: Parikh filtering is skipped for satisfied nodes (returned above);
|
||||
// a fully satisfied node has no remaining memberships to filter.
|
||||
apply_parikh_to_node(*node);
|
||||
if (node->is_currently_conflict()) {
|
||||
|
||||
if (node->is_general_conflict()) {
|
||||
++m_stats.m_num_simplify_conflict;
|
||||
return search_result::unsat;
|
||||
}
|
||||
|
|
@ -1368,21 +1365,23 @@ namespace seq {
|
|||
generate_node_length_constraints(node);
|
||||
assert_node_new_int_constraints(node);
|
||||
|
||||
if (node->is_currently_conflict())
|
||||
if (node->is_general_conflict()) {
|
||||
++m_stats.m_num_simplify_conflict;
|
||||
return search_result::unsat;
|
||||
}
|
||||
|
||||
// integer feasibility check: the solver now holds all path constraints
|
||||
// incrementally; just query the solver directly
|
||||
if (!cur_path.empty() && !check_int_feasibility()) {
|
||||
dep_tracker dep = get_subsolver_dependency(node);
|
||||
node->set_conflict(backtrack_reason::arithmetic, dep);
|
||||
node->set_general_conflict(true);
|
||||
node->set_general_conflict();
|
||||
++m_stats.m_num_arith_infeasible;
|
||||
return search_result::unsat;
|
||||
}
|
||||
|
||||
SASSERT(sr != simplify_result::satisfied || node->is_satisfied());
|
||||
SASSERT(!node->is_currently_conflict());
|
||||
SASSERT(!node->is_general_conflict());
|
||||
|
||||
if (node->is_satisfied()) {
|
||||
// Before declaring SAT, check leaf-node regex feasibility:
|
||||
|
|
@ -1391,7 +1390,7 @@ namespace seq {
|
|||
// Mirrors ZIPT NielsenNode.CheckRegex.
|
||||
dep_tracker dep;
|
||||
if (!check_leaf_regex(*node, dep)) {
|
||||
node->set_general_conflict(true);
|
||||
node->set_general_conflict();
|
||||
node->set_conflict(backtrack_reason::regex, dep);
|
||||
return search_result::unsat;
|
||||
}
|
||||
|
|
@ -1400,6 +1399,12 @@ namespace seq {
|
|||
return search_result::sat;
|
||||
}
|
||||
|
||||
// This is the first time we accept local conflicts
|
||||
// Before we only abort on global conflicts [we prefer those]
|
||||
// Also we would leave the node in an "instable" state
|
||||
if (node->is_currently_conflict())
|
||||
return search_result::unsat;
|
||||
|
||||
// depth bound check
|
||||
if (depth >= m_depth_bound)
|
||||
return search_result::unknown;
|
||||
|
|
@ -3690,8 +3695,10 @@ namespace seq {
|
|||
// We enumerate all nodes rather than having a "todo"-list, as
|
||||
// hypothetically the graph could contain cycles in the future
|
||||
for (nielsen_node const* n : m_nodes) {
|
||||
if (n->reason() == backtrack_reason::children_failed)
|
||||
if (n->reason() == backtrack_reason::children_failed) {
|
||||
SASSERT(n->m_conflict_external_literal == sat::null_literal);
|
||||
continue;
|
||||
}
|
||||
|
||||
SASSERT(n->is_currently_conflict());
|
||||
if (n->m_conflict_external_literal != sat::null_literal) {
|
||||
|
|
|
|||
|
|
@ -570,7 +570,8 @@ namespace seq {
|
|||
SASSERT(mem.m_regex != nullptr);
|
||||
m_str_mem.push_back(mem);
|
||||
}
|
||||
bool add_constraint(constraint const &ic);
|
||||
|
||||
void add_constraint(constraint const &ic);
|
||||
|
||||
vector<constraint> const& constraints() const { return m_constraints; }
|
||||
vector<constraint>& constraints() { return m_constraints; }
|
||||
|
|
@ -602,13 +603,17 @@ namespace seq {
|
|||
|
||||
// status
|
||||
bool is_general_conflict() const { return m_is_general_conflict; }
|
||||
void set_general_conflict(bool v) { m_is_general_conflict = v; }
|
||||
void set_general_conflict() {
|
||||
m_is_general_conflict = true;
|
||||
}
|
||||
|
||||
bool is_extended() const { return m_is_extended; }
|
||||
void set_extended(bool v) { m_is_extended = v; }
|
||||
void set_extended(bool v) {
|
||||
m_is_extended = v;
|
||||
}
|
||||
|
||||
bool is_currently_conflict() const {
|
||||
return m_is_general_conflict ||
|
||||
return is_general_conflict() ||
|
||||
m_conflict_external_literal != sat::null_literal ||
|
||||
(reason() != backtrack_reason::unevaluated && m_is_extended);
|
||||
}
|
||||
|
|
@ -630,11 +635,12 @@ namespace seq {
|
|||
}
|
||||
|
||||
void set_conflict(const backtrack_reason r, const dep_tracker confl) {
|
||||
if (m_conflict_internal != nullptr)
|
||||
if (m_conflict_internal != nullptr && m_conflict_external_literal == sat::null_literal)
|
||||
return;
|
||||
// We prefer internal conflicts (we need it as a justification for general conflicts)
|
||||
m_reason = r;
|
||||
m_conflict_internal = confl;
|
||||
m_conflict_external_literal = sat::null_literal;
|
||||
}
|
||||
|
||||
void set_external_conflict(sat::literal lit, dep_tracker confl) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue