#include "math/grobner/pdd_simplifier.h"
#include "math/simplex/bit_matrix.h"
namespace dd {
void simplifier::operator()() {
try {
while (!s.done() &&
(simplify_linear_step(true) ||
simplify_elim_pure_step() ||
simplify_cc_step() ||
simplify_leaf_step() ||
simplify_linear_step(false) ||
simplify_exlin() ||
false)) {
DEBUG_CODE(s.invariant(););
TRACE(dd_solver, s.display(tout););
}
}
catch (pdd_manager::mem_out) {
IF_VERBOSE(1, verbose_stream() << "simplifier memout\n");
DEBUG_CODE(s.invariant(););
}
}
struct simplifier::compare_top_var {
bool operator()(equation* a, equation* b) const {
return a->poly().var() < b->poly().var();
}
};
bool simplifier::simplify_linear_step(bool binary) {
TRACE(dd_solver, tout << "binary " << binary << "\n";);
IF_VERBOSE(3, verbose_stream() << "binary " << binary << "\n");
equation_vector linear;
for (equation* e : s.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);
}
bool simplifier::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);
struct trivial {
solver& s;
equation_vector elems;
trivial(solver& s) : s(s) {}
~trivial () {
for (auto* e : elems)
s.del_equation(e);
}
};
trivial trivial(s);
unsigned j = 0;
bool has_conflict = false;
for (equation* src : linear) {
if (has_conflict)
break;
if (s.is_trivial(*src))
continue;
unsigned v = src->poly().var();
equation_vector const& uses = use_list[v];
TRACE(dd_solver,
s.display(tout << "uses of: ", *src) << "\n";
for (equation* e : uses) s.display(tout, *e) << "\n";);
bool changed_leading_term;
bool all_reduced = true;
for (equation* dst : uses) {
if (src == dst || s.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);
s.simplify_using(*dst, *src, changed_leading_term);
if (s.is_trivial(*dst))
trivial.elems.push_back(dst);
else if (s.is_conflict(dst)) {
s.pop_equation(dst);
s.set_conflict(dst);
has_conflict = true;
}
else if (changed_leading_term) {
s.pop_equation(dst);
s.push_equation(solver::to_simplify, dst);
}
add_to_use(dst, use_list);
}
if (all_reduced)
linear[j++] = src;
}
if (!has_conflict) {
linear.shrink(j);
for (equation* src : linear) {
s.pop_equation(src);
s.push_equation(solver::solved, src);
}
}
DEBUG_CODE(s.invariant(););
return j > 0 || has_conflict;
}
bool simplifier::simplify_cc_step() {
TRACE(dd_solver, tout << "cc\n";);
IF_VERBOSE(3, verbose_stream() << "cc\n");
u_map<equation*> los;
bool reduced = false;
solver::scoped_update sc(s.m_to_simplify);
for (; sc.i < sc.sz; ++sc.i) {
auto* eq1 = sc.get();
SASSERT(eq1->state() == solver::to_simplify);
pdd p = eq1->poly();
equation* eq2 = los.insert_if_not_there(p.lo().index(), eq1);
pdd q = eq2->poly();
if (eq2 != eq1 && (p.hi().is_val() || q.hi().is_val()) && !p.lo().is_val()) {
*eq1 = p - eq2->poly();
*eq1 = s.m_dep_manager.mk_join(eq1->dep(), eq2->dep());
reduced = true;
if (s.is_trivial(*eq1)) {
s.retire(eq1);
continue;
}
else if (s.check_conflict(*eq1))
continue;
}
sc.nextj();
}
return reduced;
}
bool simplifier::simplify_leaf_step() {
TRACE(dd_solver, tout << "leaf\n";);
IF_VERBOSE(3, verbose_stream() << "leaf\n");
use_list_t use_list = get_use_list();
equation_vector leaves;
for (unsigned i = 0; i < s.m_to_simplify.size(); ++i) {
equation* e = s.m_to_simplify[i];
pdd p = e->poly();
if (p.is_val())
continue;
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);
s.simplify_using(*e2, *e, changed_leading_term);
add_to_use(e2, use_list);
if (s.is_trivial(*e2)) {
s.pop_equation(e2);
s.retire(e2);
}
else if (e2->poly().is_val()) {
s.pop_equation(e2);
s.set_conflict(*e2);
return true;
}
else if (changed_leading_term) {
s.pop_equation(e2);
s.push_equation(solver::to_simplify, e2);
}
}
}
return false;
}
bool simplifier::simplify_elim_pure_step() {
TRACE(dd_solver, tout << "pure\n";);
IF_VERBOSE(3, verbose_stream() << "pure\n");
use_list_t use_list = get_use_list();
solver::scoped_update sc(s.m_to_simplify);
bool has_solved = false;
for (; sc.i < sc.sz; ++sc.i) {
equation* e = sc.get();
pdd p = e->poly();
if (!p.is_val() && p.hi().is_val() && use_list[p.var()].size() == 1) {
s.push_equation(solver::solved, e);
has_solved = true;
}
else
sc.nextj();
}
return has_solved;
}
bool simplifier::simplify_elim_dual_step() {
use_list_t use_list = get_use_list();
bool reduced = false;
{
solver::scoped_update sc(s.m_to_simplify);
for (; sc.i < sc.sz; ++sc.i) {
equation* e = sc.get();
pdd p = e->poly();
if (e->state() != solver::to_simplify) {
reduced = true;
}
else if (!s.done() && !s.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);
s.simplify_using(*e2, *e, changed_leading_term);
if (s.is_conflict(e2)) {
s.pop_equation(e2);
s.set_conflict(e2);
}
SASSERT(!s.is_trivial(*e2) || changed_leading_term);
if (changed_leading_term) {
s.pop_equation(e2);
s.push_equation(solver::to_simplify, e2);
}
add_to_use(e2, use_list);
break;
}
reduced = true;
s.push_equation(solver::solved, e);
}
else
sc.nextj();
}
}
if (reduced) {
solver::scoped_update sc(s.m_to_simplify);
for (; sc.i < sc.sz; ++sc.i) {
equation* e = sc.get();
if (s.is_trivial(*e))
s.retire(e);
else if (e->state() == solver::to_simplify)
sc.nextj();
}
return true;
}
else
return false;
}
void simplifier::add_to_use(equation* e, use_list_t& use_list) {
unsigned_vector const& fv = e->poly().free_vars();
for (unsigned v : fv) {
use_list.reserve(v + 1);
use_list[v].push_back(e);
}
}
void simplifier::remove_from_use(equation* e, use_list_t& use_list) {
unsigned_vector const& fv = e->poly().free_vars();
for (unsigned v : fv) {
use_list.reserve(v + 1);
use_list[v].erase(e);
}
}
void simplifier::remove_from_use(equation* e, use_list_t& use_list, unsigned except_v) {
unsigned_vector const& fv = e->poly().free_vars();
for (unsigned v : fv) {
if (v != except_v) {
use_list.reserve(v + 1);
use_list[v].erase(e);
}
}
}
simplifier::use_list_t simplifier::get_use_list() {
use_list_t use_list;
for (equation * e : s.m_to_simplify) {
add_to_use(e, use_list);
}
for (equation * e : s.m_processed) {
add_to_use(e, use_list);
}
return use_list;
}
bool simplifier::simplify_exlin() {
if (s.m.get_semantics() != pdd_manager::mod2_e ||
!s.m_config.m_enable_exlin) {
return false;
}
vector<pdd> eqs, simp_eqs;
for (auto* e : s.m_to_simplify) if (!e->dep()) eqs.push_back(e->poly());
for (auto* e : s.m_processed) if (!e->dep()) eqs.push_back(e->poly());
vector<uint_set> orbits(s.m.num_vars());
init_orbits(eqs, orbits);
exlin_augment(orbits, eqs);
simplify_exlin(orbits, eqs, simp_eqs);
for (pdd const& p : simp_eqs) {
s.add(p);
}
IF_VERBOSE(10, verbose_stream() << "simp_linear " << simp_eqs.size() << "\n";);
return !simp_eqs.empty() && simplify_linear_step(false);
}
void simplifier::init_orbits(vector<pdd> const& eqs, vector<uint_set>& orbits) {
for (pdd const& p : eqs) {
auto const& fv = p.free_vars();
for (unsigned i = fv.size(); i-- > 0; ) {
orbits[fv[i]].insert(fv[i]); for (unsigned j = i; j-- > 0; ) {
orbits[fv[i]].insert(fv[j]);
orbits[fv[j]].insert(fv[i]);
}
}
}
}
void simplifier::exlin_augment(vector<uint_set> const& orbits, vector<pdd>& eqs) {
IF_VERBOSE(10, verbose_stream() << "pdd-exlin augment\n";);
unsigned nv = s.m.num_vars();
random_gen rand(s.m_config.m_random_seed);
unsigned modest_num_eqs = std::max(eqs.size(), 500u);
unsigned max_xlin_eqs = modest_num_eqs;
unsigned max_degree = 5;
TRACE(dd_solver, tout << "augment " << nv << "\n";
for (auto const& o : orbits) tout << o.num_elems() << "\n";);
vector<pdd> n_eqs;
unsigned start = rand();
for (unsigned _v = 0; _v < nv; ++_v) {
unsigned v = (_v + start) % nv;
auto const& orbitv = orbits[v];
if (orbitv.empty()) continue;
pdd pv = s.m.mk_var(v);
for (pdd p : eqs) {
if (p.degree() > max_degree) continue;
for (unsigned w : p.free_vars()) {
if (v != w && orbitv.contains(w)) {
n_eqs.push_back(pv * p);
if (n_eqs.size() > max_xlin_eqs) {
goto end_of_new_eqs;
}
break;
}
}
}
}
start = rand();
for (unsigned _v = 0; _v < nv; ++_v) {
unsigned v = (_v + start) % nv;
auto const& orbitv = orbits[v];
if (orbitv.empty()) continue;
pdd pv = s.m.mk_var(v);
for (unsigned w : orbitv) {
if (v >= w) continue;
pdd pw = s.m.mk_var(w);
for (pdd p : eqs) {
if (p.degree() + 1 > max_degree) continue;
for (unsigned u : p.free_vars()) {
if (orbits[w].contains(u) || orbits[v].contains(u)) {
n_eqs.push_back(pw * pv * p);
if (n_eqs.size() > max_xlin_eqs) {
goto end_of_new_eqs;
}
break;
}
}
}
}
}
end_of_new_eqs:
s.m_config.m_random_seed = rand();
eqs.append(n_eqs);
TRACE(dd_solver, for (pdd const& p : eqs) tout << p << "\n";);
}
void simplifier::simplify_exlin(vector<uint_set> const& orbits, vector<pdd> const& eqs, vector<pdd>& simp_eqs) {
IF_VERBOSE(10, verbose_stream() << "pdd simplify-exlin\n";);
unsigned_vector vars;
struct mon {
unsigned sz;
unsigned offset;
unsigned index;
mon(unsigned sz, unsigned offset): sz(sz), offset(offset), index(UINT_MAX) {}
mon(): sz(0), offset(0), index(UINT_MAX) {}
bool is_valid() const { return index != UINT_MAX; }
struct hash {
unsigned_vector& vars;
hash(unsigned_vector& vars):vars(vars) {}
bool operator()(mon const& m) const {
return unsigned_ptr_hash(vars.data() + m.offset, m.sz, 1);
};
};
struct eq {
unsigned_vector& vars;
eq(unsigned_vector& vars):vars(vars) {}
bool operator()(mon const& a, mon const& b) const {
if (a.sz != b.sz) return false;
for (unsigned i = 0; i < a.sz; ++i)
if (vars[a.offset+i] != vars[b.offset+i])
return false;
return true;
}
};
};
mon::hash mon_hash(vars);
mon::eq mon_eq(vars);
hashtable<mon, mon::hash, mon::eq> mon2idx(DEFAULT_HASHTABLE_INITIAL_CAPACITY, mon_hash, mon_eq);
svector<mon> idx2mon;
auto insert_mon = [&](unsigned n, unsigned const* vs) {
mon mm(n, vars.size());
vars.append(n, vs);
auto* e = mon2idx.insert_if_not_there2(mm);
if (!e->get_data().is_valid()) {
e->get_data().index = idx2mon.size();
idx2mon.push_back(e->get_data());
}
else {
vars.shrink(vars.size() - n);
}
};
for (pdd const& p : eqs) {
for (auto const& m : p) {
if (m.vars.size() <= 1) continue;
insert_mon(m.vars.size(), m.vars.data());
}
}
unsigned nv = s.m.num_vars();
for (unsigned v = 0; v < nv; ++v) {
if (!orbits[v].empty()) { insert_mon(1, &v);
}
}
IF_VERBOSE(10, verbose_stream() << "extracted monomials: " << idx2mon.size() << "\n";);
bit_matrix bm;
unsigned const_idx = idx2mon.size();
bm.reset(const_idx + 1);
for (pdd const& p : eqs) {
if (p.is_zero()) {
continue;
}
auto row = bm.add_row();
for (auto const& m : p) {
SASSERT(m.coeff.is_one());
if (m.vars.empty()) {
row.set(const_idx);
continue;
}
unsigned n = m.vars.size();
mon mm(n, vars.size());
vars.append(n, m.vars.data());
VERIFY(mon2idx.find(mm, mm));
vars.shrink(vars.size() - n);
row.set(mm.index);
}
}
TRACE(dd_solver, tout << bm << "\n";);
IF_VERBOSE(10, verbose_stream() << "bit-matrix solving\n");
bm.solve();
TRACE(dd_solver, tout << bm << "\n";);
IF_VERBOSE(10, verbose_stream() << "bit-matrix solved\n");
for (auto const& r : bm) {
bool is_linear = true;
for (unsigned c : r) {
SASSERT(r[c]);
if (c == const_idx) {
break;
}
if (idx2mon[c].sz != 1) {
is_linear = false;
break;
}
}
if (is_linear) {
pdd p = s.m.zero();
for (unsigned c : r) {
if (c == const_idx) {
p += s.m.one();
}
else {
mon const& mm = idx2mon[c];
p += s.m.mk_var(vars[mm.offset]);
}
}
if (!p.is_zero()) {
TRACE(dd_solver, tout << "new linear: " << p << "\n";);
simp_eqs.push_back(p);
}
}
}
}
}