3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-10-09 09:21:56 +00:00

add option for gcd-test to grobner

This commit is contained in:
Nikolaj Bjorner 2025-09-01 16:37:21 -07:00
parent 49703f8bba
commit e2235d81d3
6 changed files with 224 additions and 32 deletions

View file

@ -31,6 +31,7 @@ namespace nla {
void grobner::updt_params(params_ref const& p) {
smt_params_helper ph(p);
m_config.m_propagate_quotients = ph.arith_nl_grobner_propagate_quotients();
m_config.m_gcd_test = ph.arith_nl_grobner_gcd_test();
}
lp::lp_settings& grobner::lp_settings() {
@ -81,6 +82,9 @@ namespace nla {
if (propagate_quotients())
return;
if (propagate_gcd_test())
return;
}
catch (...) {
@ -99,14 +103,6 @@ namespace nla {
IF_VERBOSE(5, diagnose_pdd_miss(verbose_stream()));
}
dd::solver::equation_vector const& grobner::core_equations(bool all_eqs) {
flet<bool> _add_all(m_add_all_eqs, all_eqs);
find_nl_cluster();
if (!configure())
throw dd::pdd_manager::mem_out();
return m_solver.equations();
}
bool grobner::is_conflicting() {
for (auto eq : m_solver.equations()) {
if (is_conflicting(*eq)) {
@ -228,6 +224,76 @@ namespace nla {
return {t, offset};
}
bool grobner::propagate_gcd_test() {
if (!m_config.m_gcd_test)
return false;
unsigned changed = 0;
for (auto eq : m_solver.equations())
if (propagate_gcd_test(*eq) && ++changed >= m_solver.number_of_conflicts_to_report())
return true;
return changed > 0;
}
// x^2 - y = 0 => y mod 4 = { 0, 1 }
// x^k - y = 0 => y mod k^2 = {0, 1, .., (k-1)^k }
bool grobner::propagate_gcd_test(dd::solver::equation const& eq) {
dd::pdd p = eq.poly();
if (p.is_linear())
return false;
if (p.is_val())
return false;
auto v = p.var();
if (!c().var_is_int(v))
return false;
for (auto v : p.free_vars())
if (!c().var_is_int(v))
return false;
dd::pdd_eval eval;
eval.var2val() = [&](unsigned j) { return val(j); };
if (eval(p) == 0)
return false;
p.get_powers(m_powers);
if (m_powers.empty())
return false;
rational d(1);
for (auto const& value : p.coefficients())
d = lcm(d, denominator(value.coeff));
if (d != 1)
p *= d;
auto& m = p.manager();
auto fails_gcd_test = [&](dd::pdd const& q) {
rational g(0);
rational k(0);
for (auto const& value : q.coefficients()) {
if (value.is_constant) {
k += value.coeff;
continue;
}
g = gcd(g, value.coeff);
if (g == 1)
return false;
}
return k != 0 && !divides(g, k);
};
for (auto [x, k] : m_powers) {
SASSERT(k > 1);
bool gcd_fail = true;
dd::pdd kx = m.mk_var(x) * m.mk_val(k);
for (unsigned r = 0; gcd_fail && r < k; r++) {
dd::pdd kx_plus_r = kx + m.mk_val(r);
auto q = p.subst_pdd(x, kx_plus_r);
if (!fails_gcd_test(q))
gcd_fail = false;
}
if (gcd_fail) {
lemma_builder lemma(c(), "pdd-power");
add_dependencies(lemma, eq);
return true;
}
}
return false;
}
bool grobner::propagate_quotients() {
if (!m_config.m_propagate_quotients)
return false;
@ -249,8 +315,6 @@ namespace nla {
// z < 0 & x < 0 => -x <= -z
bool grobner::propagate_quotients(dd::solver::equation const& eq) {
dd::pdd const& p = eq.poly();
dd::pdd_eval eval;
eval.var2val() = [&](unsigned j) { return val(j); };
if (p.is_linear())
return false;
if (p.is_val())
@ -261,6 +325,8 @@ namespace nla {
for (auto v : p.free_vars())
if (!c().var_is_int(v))
return false;
dd::pdd_eval eval;
eval.var2val() = [&](unsigned j) { return val(j); };
if (eval(p) == 0)
return false;
tracked_uint_set nl_vars;
@ -577,8 +643,12 @@ namespace nla {
for (const factor& fc: fcn)
q.push_back(var(fc));
}
if (c().var_is_fixed(j))
else if (c().lra.column_has_term(j)) {
const lp::lar_term& t = c().lra.get_term(j);
for (auto k : t)
q.push_back(k.j());
}
else if (c().var_is_fixed(j))
return;
const auto& matrix = lra.A_r();
for (auto & s : matrix.m_columns[j]) {
@ -587,12 +657,18 @@ namespace nla {
continue;
m_rows.insert(row);
unsigned k = lra.get_base_column_in_row(row);
// grobner bassis does not know about integer constraints
if (lra.column_is_free(k) && !m_add_all_eqs && k != j)
continue;
// a free column over the reals can be assigned
if (lra.column_is_free(k) && k != j && !lra.var_is_int(k))
continue;
if (lra.column_is_free(k) && k != j) {
if (!lra.var_is_int(k))
continue;
// free integer columns are ignored unless m_add_all_eqs is set or we are doing gcd test.
if (!m_add_all_eqs && !m_config.m_gcd_test)
continue;
// a free integer column with integer coefficients can be assigned.
if (!m_add_all_eqs && all_of(c().lra.get_row(row), [&](auto& ri) { return ri.coeff().is_int();}))
continue;
}
CTRACE(grobner, matrix.m_rows[row].size() > c().params().arith_nl_grobner_row_length_limit(),
tout << "ignore the row " << row << " with the size " << matrix.m_rows[row].size() << "\n";);
// limits overhead of grobner equations, unless this is for extracting a complete COI of the non-satisfied subset.
@ -618,6 +694,7 @@ namespace nla {
while (!vars.empty()) {
j = vars.back();
vars.pop_back();
if (c().params().arith_nl_grobner_subs_fixed() > 0 && c().var_is_fixed_to_zero(j)) {
r = m_pdd_manager.mk_val(val_of_fixed_var_with_deps(j, zero_dep));
dep = zero_dep;

View file

@ -21,6 +21,7 @@ namespace nla {
class grobner : common {
struct config {
bool m_propagate_quotients = false;
bool m_gcd_test = false;
};
dd::pdd_manager m_pdd_manager;
dd::solver m_solver;
@ -51,6 +52,10 @@ namespace nla {
bool propagate_quotients();
bool propagate_quotients(dd::solver::equation const& eq);
svector<std::pair<unsigned, unsigned>> m_powers;
bool propagate_gcd_test();
bool propagate_gcd_test(dd::solver::equation const& eq);
std::pair<lp::lar_term, rational> linear_to_term(dd::pdd q);
void add_dependencies(lemma_builder& lemma, dd::solver::equation const& eq);
@ -74,7 +79,7 @@ namespace nla {
bool is_solved(dd::pdd const& p, unsigned& v, dd::pdd& r);
void add_eq(dd::pdd& p, u_dependency* dep);
const rational& val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep);
dd::pdd pdd_expr(const rational& c, lpvar j, u_dependency*& dep);
dd::pdd pdd_expr(const rational& c, lpvar j, u_dependency*& dep);
void display_matrix_of_m_rows(std::ostream& out) const;
std::ostream& diagnose_pdd_miss(std::ostream& out);
@ -83,6 +88,6 @@ namespace nla {
grobner(core *core);
void operator()();
void updt_params(params_ref const& p);
dd::solver::equation_vector const& core_equations(bool all_eqs);
// dd::solver::equation_vector const& core_equations(bool all_eqs);
};
}

View file

@ -19,13 +19,13 @@ std::ostream& core::print_product(const T& m, std::ostream& out) const {
bool first = true;
for (lpvar v : m) {
if (!first)
out << "*";
out << " * ";
else
first = false;
if (lp_settings().print_external_var_name())
out << "(" << lra.get_variable_name(v) << "=" << val(v) << ")";
out << lra.get_variable_name(v);
else
out << "(j" << v << " = " << val(v) << ")";
out << "j" << v;
}
return out;
}
@ -69,13 +69,13 @@ std::ostream& core::print_factor_with_vars(const factor& f, std::ostream& out) c
std::ostream& core::print_monic(const monic& m, std::ostream& out) const {
if (lp_settings().print_external_var_name())
out << "([" << m.var() << "] = " << lra.get_variable_name(m.var()) << " = " << val(m.var()) << " = ";
out << "[" << m.var() << "] := " << lra.get_variable_name(m.var()) << " := ";
else
out << "(j" << m.var() << " = " << val(m.var()) << " = ";
print_product(m.vars(), out) << ")\n";
out << "j" << m.var() << " := ";
print_product(m.vars(), out) << "\n";
return out;
}
std::ostream& core::print_bfc(const factorization& m, std::ostream& out) const {
SASSERT(m.size() == 2);
out << "( x = " << pp(m[0]) << "* y = " << pp(m[1]) << ")";
@ -85,6 +85,7 @@ std::ostream& core::print_bfc(const factorization& m, std::ostream& out) const {
std::ostream& core::print_monic_with_vars(lpvar v, std::ostream& out) const {
return print_monic_with_vars(m_emons[v], out);
}
template <typename T>
std::ostream& core::print_product_with_vars(const T& m, std::ostream& out) const {
print_product(m, out) << "\n";
@ -141,9 +142,8 @@ std::ostream& core::print_var(lpvar j, std::ostream& out) const {
}
std::ostream& core::print_monics(std::ostream& out) const {
for (auto& m : m_emons) {
print_monic_with_vars(m, out);
}
for (auto& m : m_emons)
print_monic_with_vars(m, out);
return out;
}
@ -312,8 +312,7 @@ std::ostream& core::display_row(std::ostream& out, lp::row_strip<lp::mpq> const&
}
std::ostream& core::display(std::ostream& out) {
print_monics(out);
for (unsigned i = 0; i < lra.row_count(); ++i)
display_row(out, lra.get_row(i));
for (auto& m : m_emons)
print_monic(m, out);
return out;
}