3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-10-09 09:21:56 +00:00
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
This commit is contained in:
Lev Nachmanson 2025-09-04 09:39:26 -10:00
parent af978d7c6e
commit ff39b47d1f

View file

@ -50,6 +50,7 @@ namespace nlsat {
pmanager& m_pm; pmanager& m_pm;
anum_manager& m_am; anum_manager& m_am;
std::vector<property> m_Q; // the set of properties to prove std::vector<property> m_Q; // the set of properties to prove
std::vector<symbolic_interval> m_I; // intervals per level (indexed by variable/level)
bool m_fail = false; bool m_fail = false;
bool m_Q_changed = false; // tracks mutations to m_Q for fixed-point iteration bool m_Q_changed = false; // tracks mutations to m_Q for fixed-point iteration
// Property precedence relation stored as pairs (lesser, greater) // Property precedence relation stored as pairs (lesser, greater)
@ -147,14 +148,14 @@ namespace nlsat {
{ ord_inv(resultant(p_j,p_{j+1})) for adjacent roots }. { ord_inv(resultant(p_j,p_{j+1})) for adjacent roots }.
*/ */
// Helper 1: scan input polynomials, add sgn_inv / an_del properties and collect polynomials at level m_n // Helper 1: scan input polynomials, add sgn_inv / an_del properties and collect polynomials at level m_n
void collect_level_properties(std::vector<property> & Q, std::vector<poly*> & ps_of_n_level) { void collect_level_properties(std::vector<poly*> & ps_of_n_level) {
for (unsigned i = 0; i < m_P.size(); ++i) { for (unsigned i = 0; i < m_P.size(); ++i) {
poly* p = m_P[i]; poly* p = m_P[i];
unsigned level = max_var(p); unsigned level = max_var(p);
if (level < m_n) if (level < m_n)
Q.push_back(property(prop_enum::sgn_inv_irreducible, polynomial_ref(p, m_pm), /*s_idx*/0u, /* level */ level)); m_Q.push_back(property(prop_enum::sgn_inv_irreducible, polynomial_ref(p, m_pm), /*s_idx*/0u, /* level */ level));
else if (level == m_n){ else if (level == m_n){
Q.push_back(property(prop_enum::an_del, polynomial_ref(p, m_pm), /* s_idx */ -1, level)); m_Q.push_back(property(prop_enum::an_del, polynomial_ref(p, m_pm), /* s_idx */ -1, level));
ps_of_n_level.push_back(p); ps_of_n_level.push_back(p);
} }
else { else {
@ -180,7 +181,7 @@ namespace nlsat {
// Helper 3: given collected roots (possibly unsorted), sort them, and add ord_inv(resultant(...)) // Helper 3: given collected roots (possibly unsorted), sort them, and add ord_inv(resultant(...))
// for adjacent roots coming from different polynomials. Avoid adding the same unordered pair twice. // for adjacent roots coming from different polynomials. Avoid adding the same unordered pair twice.
// Returns false on failure (e.g. when encountering an ambiguous zero resultant). // Returns false on failure (e.g. when encountering an ambiguous zero resultant).
bool add_adjacent_resultants(std::vector<std::pair<scoped_anum, poly*>> & root_vals, std::vector<property> & Q) { bool add_adjacent_resultants(std::vector<std::pair<scoped_anum, poly*>> & root_vals) {
if (root_vals.size() < 2) return true; if (root_vals.size() < 2) return true;
std::sort(root_vals.begin(), root_vals.end(), [&](auto const & a, auto const & b){ return m_am.lt(a.first, b.first); }); std::sort(root_vals.begin(), root_vals.end(), [&](auto const & a, auto const & b){ return m_am.lt(a.first, b.first); });
std::set<std::pair<unsigned,unsigned>> added_pairs; std::set<std::pair<unsigned,unsigned>> added_pairs;
@ -201,7 +202,7 @@ namespace nlsat {
NOT_IMPLEMENTED_YET();// ambiguous resultant - not handled yet NOT_IMPLEMENTED_YET();// ambiguous resultant - not handled yet
return false; return false;
} }
Q.push_back(property(prop_enum::ord_inv_irreducible, r, /*s_idx*/ -1, max_var(r))); m_Q.push_back(property(prop_enum::ord_inv_irreducible, r, /*s_idx*/ -1, max_var(r)));
} }
return true; return true;
} }
@ -211,28 +212,14 @@ namespace nlsat {
{ an_del(p) | level(p) == m_n } { an_del(p) | level(p) == m_n }
{ ord_inv(resultant(p_j,p_{j+1})) for adjacent root functions }. { ord_inv(resultant(p_j,p_{j+1})) for adjacent root functions }.
*/ */
std::vector<property> seed_properties() { void seed_properties() {
std::vector<property> Q;
std::vector<poly*> ps_of_n_level; std::vector<poly*> ps_of_n_level;
collect_level_properties(Q, ps_of_n_level); collect_level_properties(ps_of_n_level);
std::vector<std::pair<scoped_anum, poly*>> root_vals; std::vector<std::pair<scoped_anum, poly*>> root_vals;
collect_roots_for_ps(ps_of_n_level, root_vals); collect_roots_for_ps(ps_of_n_level, root_vals);
if (!add_adjacent_resultants(root_vals, Q)) { if (!add_adjacent_resultants(root_vals))
m_fail = true; m_fail = true;
return Q;
} }
return Q;
}
struct result_struct {
symbolic_interval I;
// Set E of indexed root expressions at level i for P_non_null: the root functions from E pass throug s[i]
std::vector<indexed_root_expr> E;
// omega_buckets removed: sort roots by value instead of grouping into buckets
std::vector<property> Q;
bool fail = false;
};
// Internal carrier to keep root value paired with indexed root expr // Internal carrier to keep root value paired with indexed root expr
struct root_item_t { struct root_item_t {
@ -247,7 +234,7 @@ namespace nlsat {
root_item_t& operator=(root_item_t&& other) noexcept { val = other.val; ire = other.ire; return *this; } root_item_t& operator=(root_item_t&& other) noexcept { val = other.val; ire = other.ire; return *this; }
}; };
// Compute symbolic interval I from sorted roots. Assumes roots are sorted. // Compute symbolic interval from sorted roots. Assumes roots are sorted.
void compute_interval_from_sorted_roots(unsigned i, void compute_interval_from_sorted_roots(unsigned i,
std::vector<root_item_t>& roots, std::vector<root_item_t>& roots,
symbolic_interval& I) { symbolic_interval& I) {
@ -298,7 +285,7 @@ namespace nlsat {
// directly on a sorted vector<root_item_t>. // directly on a sorted vector<root_item_t>.
// Part A of construct_interval: apply pre-conditions (line 8-11 scaffolding) // Part A of construct_interval: apply pre-conditions (line 8-11 scaffolding)
bool apply_property_rules(unsigned i, prop_enum prop_to_avoid, result_struct* rs) { bool apply_property_rules(unsigned i, prop_enum prop_to_avoid, bool has_repr) {
// Iterate until no mutation to m_Q occurs (fixed-point). We avoid copying m_Q // Iterate until no mutation to m_Q occurs (fixed-point). We avoid copying m_Q
// by using a change flag that is set by mutating helpers (add_to_Q_if_new / erase_from_Q). // by using a change flag that is set by mutating helpers (add_to_Q_if_new / erase_from_Q).
if (m_fail) return false; if (m_fail) return false;
@ -306,7 +293,7 @@ namespace nlsat {
m_Q_changed = false; m_Q_changed = false;
std::vector<property> to_refine = greatest_to_refine(i, prop_to_avoid); std::vector<property> to_refine = greatest_to_refine(i, prop_to_avoid);
for (const auto& p : to_refine) { for (const auto& p : to_refine) {
apply_pre(p, rs); apply_pre(p, has_repr);
if (m_fail) return false; if (m_fail) return false;
} }
} while (m_Q_changed && !m_fail); } while (m_Q_changed && !m_fail);
@ -314,7 +301,7 @@ namespace nlsat {
} }
// Part B of construct_interval: build (I, E, ≼) representation for level i // Part B of construct_interval: build (I, E, ≼) representation for level i
void build_representation(unsigned i, result_struct& ret) { void build_representation(unsigned i) {
std::vector<const poly*> p_non_null; std::vector<const poly*> p_non_null;
for (const auto & pr: m_Q) { for (const auto & pr: m_Q) {
if (pr.prop_tag == prop_enum::sgn_inv_irreducible && max_var(pr.poly) == i && if (pr.prop_tag == prop_enum::sgn_inv_irreducible && max_var(pr.poly) == i &&
@ -322,12 +309,14 @@ namespace nlsat {
p_non_null.push_back(pr.poly.get()); p_non_null.push_back(pr.poly.get());
} }
std::vector<root_item_t> roots; std::vector<root_item_t> roots;
collect_E_and_roots(p_non_null, i, ret.E, roots); std::vector<indexed_root_expr> E;
// Do not use omega_buckets: sort roots by their algebraic value and compute the interval collect_E_and_roots(p_non_null, i, E, roots);
// Ensure m_I can hold interval for this level
if (m_I.size() <= i) m_I.resize(i+1);
std::sort(roots.begin(), roots.end(), [&](root_item_t const& a, root_item_t const& b){ std::sort(roots.begin(), roots.end(), [&](root_item_t const& a, root_item_t const& b){
return m_am.lt(a.val, b.val); return m_am.lt(a.val, b.val);
}); });
compute_interval_from_sorted_roots(i, roots, ret.I); compute_interval_from_sorted_roots(i, roots, m_I[i]);
} }
std::vector<property> greatest_to_refine(unsigned level, prop_enum prop_to_avoid) { std::vector<property> greatest_to_refine(unsigned level, prop_enum prop_to_avoid) {
@ -430,22 +419,21 @@ namespace nlsat {
m_Q_changed = true; m_Q_changed = true;
} }
result_struct construct_interval(unsigned i) { // construct_interval: compute representation for level i and apply post rules.
result_struct ret; // Returns true on failure.
if (!apply_property_rules(i, prop_enum::sgn_inv_irreducible, nullptr)) { bool construct_interval(unsigned i) {
ret.fail = true; if (!apply_property_rules(i, prop_enum::sgn_inv_irreducible, false)) {
return ret; return true;
} }
build_representation(i, ret); build_representation(i);
apply_property_rules(i, prop_enum(prop_enum::holds), & ret); // apply post-processing that may rely on the representation being present
if (!apply_property_rules(i, prop_enum(prop_enum::holds), true))
return true;
// (moved Rule 1.1 precondition handling into apply_pre_connected) // remove properties at level i from m_Q (they are consumed by this level)
remove_level_i_from_Q(m_Q, i);
ret.Q = m_Q; return m_fail;
ret.fail = m_fail;
remove_level_i_from_Q(ret.Q, i);
return ret;
} }
// Extracted helper: handle ord_inv(discriminant_{x_{i+1}}(p)) for an_del pre-processing // Extracted helper: handle ord_inv(discriminant_{x_{i+1}}(p)) for an_del pre-processing
void add_ord_inv_discriminant_for(const property& p) { void add_ord_inv_discriminant_for(const property& p) {
@ -522,7 +510,7 @@ namespace nlsat {
} }
// Pre-processing for connected(i) (Rule 4.11) // Pre-processing for connected(i) (Rule 4.11)
void apply_pre_connected(const property & p, result_struct* rs) { void apply_pre_connected(const property & p, bool has_repr) {
TRACE(levelwise, tout << "connected:"; TRACE(levelwise, tout << "connected:";
if (p.level != static_cast<unsigned>(-1)) tout << " level=" << p.level; if (p.level != static_cast<unsigned>(-1)) tout << " level=" << p.level;
tout << std::endl;); tout << std::endl;);
@ -536,7 +524,7 @@ namespace nlsat {
} }
// p.level > 0 // p.level > 0
if (!rs) return; // no change since the interval etc is not there if (!has_repr) return; // no change since the interval etc is not there
// Rule 1.1 precondition: when processing connected(i) we must ensure the next lower level // Rule 1.1 precondition: when processing connected(i) we must ensure the next lower level
// has connected(i-1) and repr(I,s) available. Add those markers to m_Q so they propagate. // has connected(i-1) and repr(I,s) available. Add those markers to m_Q so they propagate.
@ -554,7 +542,7 @@ namespace nlsat {
if (p.level != static_cast<unsigned>(-1)) tout << " level=" << p.level; if (p.level != static_cast<unsigned>(-1)) tout << " level=" << p.level;
tout << std::endl;); tout << std::endl;);
// First try subrule 1 of Rule 4.2. If it succeeds we do not apply the fallback (subrule 2). // First try subrule 1 of Rule 4.2. If it succeeds we do not apply the fallback (subrule 2).
if (try_non_null_via_coeffs(p, nullptr)) if (try_non_null_via_coeffs(p))
return; return;
// fallback: apply the first subrule // fallback: apply the first subrule
apply_pre_non_null_fallback(p); apply_pre_non_null_fallback(p);
@ -574,7 +562,7 @@ namespace nlsat {
// if c_j evaluates non-zero at the sample and we already have sgn_inv(c_j) in m_Q, // if c_j evaluates non-zero at the sample and we already have sgn_inv(c_j) in m_Q,
// then non_null(p) holds on the region represented by 'rs' (if provided). // then non_null(p) holds on the region represented by 'rs' (if provided).
// Returns true if non_null was established and the property p was removed. // Returns true if non_null was established and the property p was removed.
bool try_non_null_via_coeffs(const property& p, result_struct* rs) { bool try_non_null_via_coeffs(const property& p) {
if (have_non_zero_const(p.poly, p.level)) { if (have_non_zero_const(p.poly, p.level)) {
TRACE(levelwise, tout << "have a non-zero const coefficient\n";); TRACE(levelwise, tout << "have a non-zero const coefficient\n";);
erase_from_Q(p); erase_from_Q(p);
@ -654,12 +642,12 @@ namespace nlsat {
erase_from_Q(p); erase_from_Q(p);
} }
void apply_pre(const property& p, result_struct* rs) { void apply_pre(const property& p, bool has_repr) {
TRACE(levelwise, display(tout << "property p:", p) << std::endl;); TRACE(levelwise, display(tout << "property p:", p) << std::endl;);
if (p.prop_tag == prop_enum::an_del) if (p.prop_tag == prop_enum::an_del)
apply_pre_an_del(p); apply_pre_an_del(p);
else if (p.prop_tag == prop_enum::connected) else if (p.prop_tag == prop_enum::connected)
apply_pre_connected(p, rs ); apply_pre_connected(p, has_repr );
else if (p.prop_tag == prop_enum::non_null) else if (p.prop_tag == prop_enum::non_null)
apply_pre_non_null(p); apply_pre_non_null(p);
else else
@ -675,18 +663,14 @@ namespace nlsat {
tout << "max_var:" << m_pm.max_var(p) << std::endl; tout << "max_var:" << m_pm.max_var(p) << std::endl;
} }
); );
std::vector<symbolic_interval> ret; seed_properties(); // initializes m_Q as the set of properties on level m_n
m_Q = seed_properties(); // Q is the set of properties on level m_n apply_property_rules(m_n, prop_enum::_count, false); // reduce the level by one to be consumed by construct_interval
apply_property_rules(m_n, prop_enum::_count, nullptr); // reduce the level by one to be consumed by construct_interval
for (unsigned i = m_n; --i > 0; ) { for (unsigned i = m_n; --i > 0; ) {
auto result = construct_interval(i); if (!construct_interval(i))
if (result.fail)
return std::vector<symbolic_interval>(); // return empty return std::vector<symbolic_interval>(); // return empty
ret.push_back(result.I);
m_Q = result.Q;
} }
return ret; // the order is reversed! return m_I; // the order of intervals is reversed
} }
bool dominates(const property& a, const property& b) const { bool dominates(const property& a, const property& b) const {