3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-02-24 09:11:17 +00:00
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2026-01-22 19:50:23 -08:00
parent 2bb5829c1b
commit 426c02a22c
2 changed files with 128 additions and 85 deletions

View file

@ -147,6 +147,13 @@ namespace nla {
pop_propagation(m_bounds.size());
}
m_constraint_index.reset();
for (auto & ivs : m_intervals) {
for (auto iv : ivs) {
m_di.del(*iv);
dealloc(iv);
}
ivs.reset();
}
}
void stellensatz2::init_solver() {
@ -514,7 +521,8 @@ namespace nla {
auto new_ci = add(ci, p_is_0);
TRACE(arith, display_constraint(tout << "j" << x << " ", ci);
display_constraint(tout << "reduced ", new_ci);
display_constraint(tout, p_is_0));
display_constraint(tout, p_is_0));
new_ci = factor(new_ci);
return new_ci;
}
@ -966,6 +974,8 @@ namespace nla {
is_sat = resolve_conflict();
else if (should_propagate())
propagate();
else if (repair())
continue;
else if (!decide())
is_sat = l_true;
}
@ -1025,12 +1035,13 @@ namespace nla {
//
lbool stellensatz2::resolve_conflict() {
SASSERT(is_conflict());
TRACE(arith, tout << "resolving conflict: "; display_constraint(tout, m_conflict); display(tout););
TRACE(arith, tout << "resolving conflict: " << m_conflict_dep << "\n"; display_constraint(tout, m_conflict);
display(tout););
m_conflict_marked_ci.reset();
unsigned conflict_level = 0;
for (auto ci : m_conflict_dep) {
mark_dependencies(ci);
m_conflict_marked_ci.insert(ci);
conflict_level = std::max(conflict_level, m_levels[ci]);
}
@ -1046,7 +1057,7 @@ namespace nla {
mark_dependencies(ci);
m_conflict_marked_ci.remove(ci);
m_conflict = resolve(m_conflict, ci);
//m_conflict = resolve(m_conflict, ci);
if (conflict_level == 0)
m_core.push_back(ci);
@ -1056,14 +1067,16 @@ namespace nla {
tout << "is_decision: " << found_decision << "\n"; display_constraint(tout, ci);
tout << "new conflict: "; display_constraint(tout, m_conflict););
}
SASSERT(found_decision == (conflict_level != 0));
#if 0
if (constraint_is_conflict(m_conflict)) {
TRACE(arith, tout << "Conflict " << m_conflict << "\n");
m_core.push_back(m_conflict);
reset_conflict();
return l_false;
}
if (conflict_level == 0) {
#endif
SASSERT(found_decision == (conflict_level != 0));
if (!found_decision) {
for (auto ci : m_conflict_marked_ci)
m_core.push_back(ci);
reset_conflict();
@ -1348,17 +1361,9 @@ namespace nla {
//
// Attempt to repair variables in some order
// If hitting a variable that cannot be repaired, create a decision based on the value conflict.
// Attempts to repair a variable can result in a new conflict obtained from vanishing polynomials
// or conflicting bounds. The conflicts are assumed to contain variables of lower levels and
// repair of those constraints are re-attempted.
// If a variable is in a false constraint that cannot be repaired, pick a non-fixed
// variable from the constraint and tighten its bound.
//
bool stellensatz2::decide() {
rational value;
lp::lconstraint_kind k;
lpvar w;
bool stellensatz2::repair() {
init_levels();
unsigned num_fixed = 0;
@ -1366,9 +1371,10 @@ namespace nla {
unsigned num_rounds = 0;
unsigned max_rounds = num_levels * 5;
unsigned constraint_lim = m_constraints.size();
unsigned constraint_size = m_constraints.size();
m_tabu.reset();
for (unsigned level = 0; level < num_levels && c().reslim().inc() && num_rounds < max_rounds; ++level) {
w = m_level2var[level];
lpvar w = m_level2var[level];
++num_rounds;
lp::constraint_index conflict = repair_variable(w);
TRACE(arith, display_constraint(tout << "repair lvl:" << level << " v" << w << ": ", conflict)
@ -1392,30 +1398,48 @@ namespace nla {
auto p = m_constraints[conflict].p;
SASSERT(!p.free_vars().empty());
if (!p.free_vars().contains(w))
if (!p.free_vars().contains(w))
// backtrack decision to max variable in ci
level = std::min(max_level(m_constraints[conflict]) - 1, level);
level = std::min(max_level(m_constraints[conflict]) - 1, level);
}
if (m_num_scopes < 4 && find_split(w, value, k)) {
SASSERT(k == lp::lconstraint_kind::LE || k == lp::lconstraint_kind::GE);
bool is_upper = k == lp::lconstraint_kind::LE;
SASSERT(!is_upper || !has_lo(w) || lo_val(w) <= value);
SASSERT( is_upper || !has_hi(w) || hi_val(w) >= value);
add_constraint(pddm.mk_var(w) - value, k, assumption_justification());
m_values[w] = value;
TRACE(arith, tout << "decide v" << w << " " << k << " " << value << "\n");
IF_VERBOSE(3, verbose_stream() << "decide v" << w << " " << k << " " << value << "\n");
SASSERT(in_bounds(w));
SASSERT(well_formed_var(w));
SASSERT(well_formed_last_bound());
++c().lp_settings().stats().m_stellensatz.m_num_decisions;
// verbose_stream() << "split " << m_num_scopes << "\n";
if (num_rounds >= max_rounds)
return false;
if (constraint_size < m_constraints.size()) {
// TODO check sat wrt linear constraints
return true;
}
return false;
}
// If hitting a variable that cannot be repaired, create a decision based on the value conflict.
// Attempts to repair a variable can result in a new conflict obtained from vanishing polynomials
// or conflicting bounds. The conflicts are assumed to contain variables of lower levels and
// repair of those constraints are re-attempted.
// If a variable is in a false constraint that cannot be repaired, pick a non-fixed
// variable from the constraint and tighten its bound.
//
bool stellensatz2::decide() {
rational value;
lp::lconstraint_kind k;
lpvar w;
if (!find_split(w, value, k))
return false;
SASSERT(k == lp::lconstraint_kind::LE || k == lp::lconstraint_kind::GE);
bool is_upper = k == lp::lconstraint_kind::LE;
SASSERT(!is_upper || !has_lo(w) || lo_val(w) <= value);
SASSERT(is_upper || !has_hi(w) || hi_val(w) >= value);
add_constraint(pddm.mk_var(w) - value, k, assumption_justification());
m_values[w] = value;
TRACE(arith, tout << "decide v" << w << " " << k << " " << value << "\n");
IF_VERBOSE(3, verbose_stream() << "decide v" << w << " " << k << " " << value << "\n");
SASSERT(in_bounds(w));
SASSERT(well_formed_var(w));
SASSERT(well_formed_last_bound());
++c().lp_settings().stats().m_stellensatz.m_num_decisions;
// verbose_stream() << "split " << m_num_scopes << "\n";
return true;
}
unsigned stellensatz2::max_level(constraint const &c) const {
unsigned level = 0;
@ -1789,8 +1813,10 @@ namespace nla {
found = true;
}
}
if (found)
if (found) {
++m_split_count[v];
IF_VERBOSE(1, verbose_stream() << "split v" << v << " " << m_split_count[v] << "\n");
}
return found;
}
@ -1923,7 +1949,7 @@ namespace nla {
out << "Polynomial queue (qhead=" << m_prop_qhead << "):\n";
for (unsigned i = 0; i < m_polynomial_queue.size(); ++i) {
out << " [" << i << "]" << (i < m_prop_qhead ? " (processed)" : "") << " "
<< m_polynomial_queue[i] << "\n";
<< m_polynomial_queue[i].first << "\n";
}
// Display intervals
@ -2220,32 +2246,12 @@ namespace nla {
// 2 new bound is added, then update intervals and queue up for propagation
auto const &b = m_bounds[ci];
auto &m = m_di.num_manager();
scoped_dep_interval new_iv(m_di);
bool is_strict = k == lp::lconstraint_kind::GT;
if (b.q.is_var()) {
auto const &iv = get_interval(b.q);
m_di.set_lower_is_inf(new_iv, false);
m_di.set_lower(new_iv, b.m_value);
m_di.set_lower_is_open(new_iv, is_strict);
m_di.set_lower_dep(new_iv, b.d);
if (!iv.m_upper_inf)
m_di.copy_upper_bound<dep_intervals::with_deps>(iv, new_iv);
if (update_interval(new_iv, b.q))
m_polynomial_queue.push_back(b.q);
}
if (b.mq.is_var()) {
auto const &iv = get_interval(b.mq);
m_di.set_upper_is_inf(new_iv, false);
m_di.set_upper_is_open(new_iv, is_strict);
m_di.set_upper_dep(new_iv, b.d);
m_di.set_upper(new_iv, -b.m_value);
if (!iv.m_lower_inf)
m_di.copy_lower_bound<dep_intervals::with_deps>(iv, new_iv);
if (update_interval(new_iv, b.mq))
m_polynomial_queue.push_back(b.mq);
}
if (b.q.is_var())
m_polynomial_queue.push_back({b.q, ci});
else if (b.mq.is_var())
m_polynomial_queue.push_back({b.mq, ci});
}
void stellensatz2::insert_parents(dd::pdd const &p, lp::constraint_index ci) {
@ -2305,7 +2311,10 @@ namespace nla {
}
while (m_interval_trail.size() > s.interval_lim) {
auto p_index = m_interval_trail.back();
auto p_index = m_interval_trail.back();
auto iv = m_intervals[p_index].back();
m_di.del(*iv);
dealloc(iv);
m_intervals[p_index].pop_back();
m_interval_trail.pop_back();
}
@ -2314,8 +2323,10 @@ namespace nla {
}
void stellensatz2::propagate() {
while (m_prop_qhead < m_polynomial_queue.size() && !is_conflict() && c().reslim().inc())
propagate_intervals(m_polynomial_queue[m_prop_qhead++]);
while (m_prop_qhead < m_polynomial_queue.size() && !is_conflict() && c().reslim().inc()) {
auto [p, ci] = m_polynomial_queue[m_prop_qhead++];
propagate_intervals(p, ci);
}
}
bool stellensatz2::is_better(dep_interval const &new_iv, dep_interval const &old_iv) {
@ -2351,7 +2362,7 @@ namespace nla {
bool stellensatz2::update_interval(dep_interval const &new_iv, dd::pdd const &p) {
SASSERT(!p.is_val());
// SASSERT(!m_di.is_empty(new_iv));
SASSERT(!m_di.is_empty(new_iv));
auto &old_iv = get_interval(p);
if (!is_better(new_iv, old_iv))
@ -2363,13 +2374,47 @@ namespace nla {
return true;
}
void stellensatz2::propagate_intervals(dd::pdd const &p) {
auto const &ivp = get_interval(p);
if (m_di.is_empty(ivp)) {
m_conflict_dep.reset();
m_dm.linearize(ivp.m_lower_dep, m_conflict_dep);
m_dm.linearize(ivp.m_upper_dep, m_conflict_dep);
void stellensatz2::propagate_intervals(dd::pdd const &p, lp::constraint_index ci) {
if (is_conflict())
return;
if (ci != lp::null_ci) {
auto [poly, k] = m_constraints[ci];
auto const &b = m_bounds[ci];
auto &m = m_di.num_manager();
scoped_dep_interval new_iv(m_di);
bool is_strict = k == lp::lconstraint_kind::GT;
if (p == b.q) {
auto const &iv = get_interval(b.q);
m_di.set_lower_is_inf(new_iv, false);
m_di.set_lower(new_iv, b.m_value);
m_di.set_lower_is_open(new_iv, is_strict);
m_di.set_lower_dep(new_iv, b.d);
if (!iv.m_upper_inf)
m_di.copy_upper_bound<dep_intervals::with_deps>(iv, new_iv);
if (m_di.is_empty(new_iv)) {
m_dm.linearize(new_iv->m_lower_dep, m_conflict_dep);
m_dm.linearize(new_iv->m_upper_dep, m_conflict_dep);
return;
}
if (!update_interval(new_iv, b.q))
return;
}
else {
auto const &iv = get_interval(b.mq);
m_di.set_upper_is_inf(new_iv, false);
m_di.set_upper_is_open(new_iv, is_strict);
m_di.set_upper_dep(new_iv, b.d);
m_di.set_upper(new_iv, -b.m_value);
if (!iv.m_lower_inf)
m_di.copy_lower_bound<dep_intervals::with_deps>(iv, new_iv);
if (m_di.is_empty(new_iv)) {
m_dm.linearize(new_iv->m_lower_dep, m_conflict_dep);
m_dm.linearize(new_iv->m_upper_dep, m_conflict_dep);
return;
}
if (!update_interval(new_iv, b.mq))
return;
}
}
// check constraints
@ -2394,20 +2439,25 @@ namespace nla {
svector<lp::constraint_index> cs;
if (constraint_is_propagating(iv_p, iv_mq, x, cs, k, value))
propagate_constraint(x, k, value, ci, cs);
if (is_conflict())
return;
}
// for each parent of p, propagate updated intervals
m_parents.reserve(p.index() + 1);
for (auto parent : m_parents[p.index()]) {
SASSERT(!parent.is_val());
SASSERT(!is_conflict());
scoped_dep_interval new_iv(m_di);
auto &x = get_interval(pddm.mk_var(parent.var()));
auto &lo = get_interval(parent.lo());
auto &hi = get_interval(parent.hi());
SASSERT(!m_di.is_empty(x));
SASSERT(!m_di.is_empty(lo));
SASSERT(!m_di.is_empty(hi));
calculate_interval(new_iv, x, lo, hi);
if (update_interval(new_iv, parent))
m_polynomial_queue.push_back(parent);
m_polynomial_queue.push_back({parent, lp::null_ci});
}
}
}

View file

@ -208,6 +208,7 @@ namespace nla {
void propagate();
bool decide();
bool repair();
lbool search();
lbool resolve_conflict();
void backtrack(lp::constraint_index ci, svector<lp::constraint_index> const &deps);
@ -228,14 +229,6 @@ namespace nla {
void retrieve_interval(scoped_dep_interval &out, dd::pdd const &p);
void retrieve_interval(scoped_dep_interval &out, lpvar v);
void set_conflict(lp::constraint_index ci) {
m_conflict = ci;
}
void set_conflict_var(lpvar v) {
m_conflict_dep.push_back(lo_constraint(v));
m_conflict_dep.push_back(hi_constraint(v));
m_conflict = resolve_variable(v, lo_constraint(v), hi_constraint(v));
}
void reset_conflict() { m_conflict = lp::null_ci; m_conflict_dep.reset(); }
bool is_conflict() const { return !m_conflict_dep.empty(); }
bool is_decision(justification const& j) const { return std::holds_alternative<assumption_justification>(j); }
@ -260,12 +253,12 @@ namespace nla {
vector<dd::pdd> m_parent_trail;
vector<vector<dd::pdd>> m_parents;
vector<vector<factor_prop>> m_factors;
vector<dd::pdd> m_polynomial_queue;
vector<std::pair<dd::pdd, lp::constraint_index>> m_polynomial_queue;
unsigned_vector m_interval_trail;
unsigned_vector m_factor_trail;
unsigned_vector m_parent_constraints_trail;
vector<svector<lp::constraint_index>> m_parent_constraints;
vector<scoped_ptr_vector<dep_interval>> m_intervals;
vector<ptr_vector<dep_interval>> m_intervals;
bool_vector m_is_parent;
void push_bound(lp::constraint_index ci);
@ -356,7 +349,7 @@ namespace nla {
bool is_better(dep_interval const &new_iv, dep_interval const &old_iv);
bool update_interval(dep_interval const &new_iv, dd::pdd const &p);
dep_interval const &get_interval(dd::pdd const &p);
void propagate_intervals(dd::pdd const &p);
void propagate_intervals(dd::pdd const &p, lp::constraint_index ci);
void propagate_constraint(lpvar x, lp::lconstraint_kind k, rational const &value, lp::constraint_index ci, svector<lp::constraint_index> &cs);
// constraints