3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-07-18 02:16:40 +00:00

add bit-matrix, avoid flattening and/or after bit-blasting, split pdd_grobner into solver/simplifier, add xlin, add smtfd option for incremental mode logic

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2020-01-01 20:14:12 -08:00
parent 09dbacdf50
commit 1d0572354b
17 changed files with 991 additions and 386 deletions

View file

@ -12,8 +12,10 @@
--*/
#include "math/grobner/pdd_solver.h"
#include "math/grobner/pdd_simplifier.h"
#include "util/uint_set.h"
namespace dd {
/***
@ -71,47 +73,6 @@ namespace dd {
Justification:
- elements in S have no variables watched
- elements in A are always reduced modulo all variables above the current x_i.
TBD:
Linear Elimination:
- comprises of a simplification pass that puts linear equations in to_processed
- so before simplifying with respect to the variable ordering, eliminate linear equalities.
Extended Linear Simplification (as exploited in Bosphorus AAAI 2019):
- multiply each polynomial by one variable from their orbits.
- The orbit of a varible are the variables that occur in the same monomial as it in some polynomial.
- The extended set of polynomials is fed to a linear Gauss Jordan Eliminator that extracts
additional linear equalities.
- Bosphorus uses M4RI to perform efficient GJE to scale on large bit-matrices.
Long distance vanishing polynomials (used by PolyCleaner ICCAD 2019):
- identify polynomials p, q, such that p*q = 0
- main case is half-adders and full adders (p := x + y, q := x * y) over GF2
because (x+y)*x*y = 0 over GF2
To work beyond GF2 we would need to rely on simplification with respect to asserted equalities.
The method seems rather specific to hardware multipliers so not clear it is useful to
generalize.
- find monomials that contain pairs of vanishing polynomials, transitively
withtout actually inlining.
Then color polynomial variables w by p, resp, q if they occur in polynomial equalities
w - r = 0, such that all paths in r contain a node colored by p, resp q.
polynomial variables that get colored by both p and q can be set to 0.
When some variable gets colored, other variables can be colored.
- We can walk pdd nodes by level to perform coloring in a linear sweep.
PDD nodes that are equal to 0 using some equality are marked as definitions.
First walk definitions to search for vanishing polynomial pairs.
Given two definition polynomials d1, d2, it must be the case that
level(lo(d1)) = level(lo(d1)) for the polynomial lo(d1)*lo(d2) to be vanishing.
Then starting from the lowest level examine pdd nodes.
Let the current node be called p, check if the pdd node p is used in an equation
w - r = 0. In which case, w inherits the labels from r.
Otherwise, label the node by the intersection of vanishing polynomials from lo(p) and hi(p).
Eliminating multiplier variables, but not adders [Kaufmann et al FMCAD 2019 for GF2];
- Only apply GB saturation with respect to variables that are part of multipliers.
- Perhaps this amounts to figuring out whether a variable is used in an xor or more
*/
@ -165,323 +126,10 @@ namespace dd {
}
void solver::simplify() {
try {
while (!done() &&
(simplify_linear_step(true) ||
simplify_elim_pure_step() ||
simplify_cc_step() ||
simplify_leaf_step() ||
simplify_linear_step(false) ||
/*simplify_elim_dual_step() ||*/
false)) {
DEBUG_CODE(invariant(););
TRACE("dd.solver", display(tout););
}
}
catch (pdd_manager::mem_out) {
// done reduce
DEBUG_CODE(invariant(););
}
simplifier s(*this);
s();
}
struct solver::compare_top_var {
bool operator()(equation* a, equation* b) const {
return a->poly().var() < b->poly().var();
}
};
bool solver::simplify_linear_step(bool binary) {
TRACE("dd.solver", tout << "binary " << binary << "\n";);
IF_VERBOSE(2, verbose_stream() << "binary " << binary << "\n");
equation_vector linear;
for (equation* e : m_to_simplify) {
pdd p = e->poly();
if (binary) {
if (p.is_binary()) linear.push_back(e);
}
else if (p.is_linear()) {
linear.push_back(e);
}
}
return simplify_linear_step(linear);
}
/**
\brief simplify linear equations by using top variable as solution.
The linear equation is moved to set of solved equations.
*/
bool solver::simplify_linear_step(equation_vector& linear) {
if (linear.empty()) return false;
use_list_t use_list = get_use_list();
compare_top_var ctv;
std::stable_sort(linear.begin(), linear.end(), ctv);
equation_vector trivial;
unsigned j = 0;
bool has_conflict = false;
for (equation* src : linear) {
if (has_conflict) {
break;
}
unsigned v = src->poly().var();
equation_vector const& uses = use_list[v];
TRACE("dd.solver",
display(tout << "uses of: ", *src) << "\n";
for (equation* e : uses) {
display(tout, *e) << "\n";
});
bool changed_leading_term;
bool all_reduced = true;
for (equation* dst : uses) {
if (src == dst || is_trivial(*dst)) {
continue;
}
pdd q = dst->poly();
if (!src->poly().is_binary() && !q.is_linear()) {
all_reduced = false;
continue;
}
remove_from_use(dst, use_list, v);
simplify_using(*dst, *src, changed_leading_term);
if (is_trivial(*dst)) {
trivial.push_back(dst);
}
else if (is_conflict(dst)) {
pop_equation(dst);
set_conflict(dst);
has_conflict = true;
}
else if (changed_leading_term) {
pop_equation(dst);
push_equation(to_simplify, dst);
}
// v has been eliminated.
SASSERT(!m.free_vars(dst->poly()).contains(v));
add_to_use(dst, use_list);
}
if (all_reduced) {
linear[j++] = src;
}
}
if (!has_conflict) {
linear.shrink(j);
for (equation* src : linear) {
pop_equation(src);
push_equation(solved, src);
}
}
for (equation* e : trivial) {
del_equation(e);
}
DEBUG_CODE(invariant(););
return j > 0 || has_conflict;
}
/**
\brief simplify using congruences
replace pair px + q and ry + q by
px + q, px - ry
since px = ry
*/
bool solver::simplify_cc_step() {
TRACE("dd.solver", tout << "cc\n";);
IF_VERBOSE(2, verbose_stream() << "cc\n");
u_map<equation*> los;
bool reduced = false;
unsigned j = 0;
for (equation* eq1 : m_to_simplify) {
SASSERT(eq1->state() == to_simplify);
pdd p = eq1->poly();
auto* e = los.insert_if_not_there2(p.lo().index(), eq1);
equation* eq2 = e->get_data().m_value;
pdd q = eq2->poly();
if (eq2 != eq1 && (p.hi().is_val() || q.hi().is_val()) && !p.lo().is_val()) {
*eq1 = p - eq2->poly();
*eq1 = m_dep_manager.mk_join(eq1->dep(), eq2->dep());
reduced = true;
if (is_trivial(*eq1)) {
retire(eq1);
continue;
}
else if (check_conflict(*eq1)) {
continue;
}
}
m_to_simplify[j] = eq1;
eq1->set_index(j++);
}
m_to_simplify.shrink(j);
return reduced;
}
/**
\brief remove ax+b from p if x occurs as a leaf in p and a is a constant.
*/
bool solver::simplify_leaf_step() {
TRACE("dd.solver", tout << "leaf\n";);
IF_VERBOSE(2, verbose_stream() << "leaf\n");
use_list_t use_list = get_use_list();
equation_vector leaves;
for (unsigned i = 0; i < m_to_simplify.size(); ++i) {
equation* e = m_to_simplify[i];
pdd p = e->poly();
if (!p.hi().is_val()) {
continue;
}
leaves.reset();
for (equation* e2 : use_list[p.var()]) {
if (e != e2 && e2->poly().var_is_leaf(p.var())) {
leaves.push_back(e2);
}
}
for (equation* e2 : leaves) {
bool changed_leading_term;
remove_from_use(e2, use_list);
simplify_using(*e2, *e, changed_leading_term);
add_to_use(e2, use_list);
if (is_trivial(*e2)) {
pop_equation(e2);
retire(e2);
}
else if (e2->poly().is_val()) {
pop_equation(e2);
set_conflict(*e2);
return true;
}
else if (changed_leading_term) {
pop_equation(e2);
push_equation(to_simplify, e2);
}
}
}
return false;
}
/**
\brief treat equations as processed if top variable occurs only once.
*/
bool solver::simplify_elim_pure_step() {
TRACE("dd.solver", tout << "pure\n";);
IF_VERBOSE(2, verbose_stream() << "pure\n");
use_list_t use_list = get_use_list();
unsigned j = 0;
for (equation* e : m_to_simplify) {
pdd p = e->poly();
if (!p.is_val() && p.hi().is_val() && use_list[p.var()].size() == 1) {
push_equation(solved, e);
}
else {
m_to_simplify[j] = e;
e->set_index(j++);
}
}
if (j != m_to_simplify.size()) {
m_to_simplify.shrink(j);
return true;
}
return false;
}
/**
\brief
reduce equations where top variable occurs only twice and linear in one of the occurrences.
*/
bool solver::simplify_elim_dual_step() {
use_list_t use_list = get_use_list();
unsigned j = 0;
bool reduced = false;
for (unsigned i = 0; i < m_to_simplify.size(); ++i) {
equation* e = m_to_simplify[i];
pdd p = e->poly();
// check that e is linear in top variable.
if (e->state() != to_simplify) {
reduced = true;
}
else if (!done() && !is_trivial(*e) && p.hi().is_val() && use_list[p.var()].size() == 2) {
for (equation* e2 : use_list[p.var()]) {
if (e2 == e) continue;
bool changed_leading_term;
remove_from_use(e2, use_list);
simplify_using(*e2, *e, changed_leading_term);
if (is_conflict(e2)) {
pop_equation(e2);
set_conflict(e2);
}
// when e2 is trivial, leading term is changed
SASSERT(!is_trivial(*e2) || changed_leading_term);
if (changed_leading_term) {
pop_equation(e2);
push_equation(to_simplify, e2);
}
add_to_use(e2, use_list);
break;
}
reduced = true;
push_equation(solved, e);
}
else {
m_to_simplify[j] = e;
e->set_index(j++);
}
}
if (reduced) {
// clean up elements in m_to_simplify
// they may have moved.
m_to_simplify.shrink(j);
j = 0;
for (equation* e : m_to_simplify) {
if (is_trivial(*e)) {
retire(e);
}
else if (e->state() == to_simplify) {
m_to_simplify[j] = e;
e->set_index(j++);
}
}
m_to_simplify.shrink(j);
return true;
}
else {
return false;
}
}
void solver::add_to_use(equation* e, use_list_t& use_list) {
unsigned_vector const& fv = m.free_vars(e->poly());
for (unsigned v : fv) {
use_list.reserve(v + 1);
use_list[v].push_back(e);
}
}
void solver::remove_from_use(equation* e, use_list_t& use_list) {
unsigned_vector const& fv = m.free_vars(e->poly());
for (unsigned v : fv) {
use_list.reserve(v + 1);
use_list[v].erase(e);
}
}
void solver::remove_from_use(equation* e, use_list_t& use_list, unsigned except_v) {
unsigned_vector const& fv = m.free_vars(e->poly());
for (unsigned v : fv) {
if (v != except_v) {
use_list.reserve(v + 1);
use_list[v].erase(e);
}
}
}
solver::use_list_t solver::get_use_list() {
use_list_t use_list;
for (equation * e : m_to_simplify) {
add_to_use(e, use_list);
}
for (equation * e : m_processed) {
add_to_use(e, use_list);
}
return use_list;
}
void solver::superpose(equation const & eq) {
for (equation* target : m_processed) {