3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

more efficient sign lemma

Signed-off-by: Lev <levnach@hotmail.com>
This commit is contained in:
Lev 2019-01-08 23:16:33 -08:00 committed by Lev Nachmanson
parent 4f2eb0b4eb
commit 0d5ca4edfe
4 changed files with 221 additions and 98 deletions

View file

@ -267,8 +267,8 @@ class theory_lra::imp {
struct switcher {
theory_lra::imp& m_th_imp;
scoped_ptr<nra::solver>* m_nra;
scoped_ptr<nla::solver>* m_nla;
bool m_use_nla;
scoped_ptr<nla::solver>* m_nla;
bool m_use_nla;
switcher(theory_lra::imp& i): m_th_imp(i), m_nra(nullptr), m_nla(nullptr) {
}

View file

@ -20,9 +20,13 @@ namespace nla {
public:
// constructors
monomial(lp::var_index v, unsigned sz, lp::var_index const* vs):
m_v(v), m_vs(sz, vs) {}
m_v(v), m_vs(sz, vs) {
std::sort(m_vs.begin(), m_vs.end());
}
monomial(lp::var_index v, const svector<lp::var_index> &vs):
m_v(v), m_vs(vs) {}
m_v(v), m_vs(vs) {
std::sort(m_vs.begin(), m_vs.end());
}
monomial() {}
unsigned var() const { return m_v; }

View file

@ -29,6 +29,7 @@ namespace nla {
typedef lp::lconstraint_kind llc;
struct solver::imp {
typedef lp::lar_base_constraint lpcon;
//fields
@ -39,12 +40,133 @@ struct solver::imp {
// this field is used for the push/pop operations
unsigned_vector m_monomials_counts;
lp::lar_solver& m_lar_solver;
std::unordered_map<lpvar, svector<lpvar>> m_monomials_containing_var;
// m_var_to_its_monomial[m_monomials[i].var()] = i
std::unordered_map<lpvar, unsigned> m_var_to_its_monomial;
lp::explanation * m_expl;
lemma * m_lemma;
unsigned_vector m_to_refine;
std::unordered_map<unsigned_vector, unsigned, hash_svector> m_mkeys; // the key is the sorted vars of a monomial
struct const_iterator_equiv_mon {
// fields
const unsigned_vector m_vars;
vector<unsigned_vector::const_iterator> m_offsets;
const imp& m_imp;
bool m_done;
// constructor for begin()
const_iterator_equiv_mon(const unsigned_vector& vars,
vector<unsigned_vector::const_iterator> offsets,
const imp& i) : m_vars(vars),
m_offsets(offsets),
m_imp(i),
m_done(false) {}
// constructor for end()
const_iterator_equiv_mon( const imp& i) :m_imp(i),
m_done(true) {}
//typedefs
typedef const_iterator_equiv_mon self_type;
typedef unsigned value_type;
typedef unsigned reference;
typedef int difference_type;
typedef std::forward_iterator_tag iterator_category;
void advance() {
if (m_done)
return;
unsigned k = 0;
for (; k < m_offsets.size(); k++) {
auto & it = m_offsets[k];
it++;
const auto & evars = m_imp.abs_eq_vars(m_vars[k]);
if (it == evars.end()) {
it = evars.begin();
} else {
break;
}
}
if (k == m_vars.size()) {
m_done = true;
}
}
unsigned_vector get_key() const {
unsigned_vector r;
for(const auto& it : m_offsets){
r.push_back(*it);
}
std::sort(r.begin(), r.end());
TRACE("nla_solver", tout << "r = "; m_imp.print_product_with_vars(r, tout););
return r;
}
unsigned get_monomial() const {
unsigned_vector key = get_key();
return m_imp.find_monomial(key);
}
self_type operator++() {self_type i = *this; operator++(1); return i;}
self_type operator++(int) { advance(); return *this; }
bool operator==(const self_type &other) const { return m_done == other.m_done;}
bool operator!=(const self_type &other) const { return ! (*this == other); }
reference operator*() const {
unsigned i = get_monomial();
TRACE("nla_solver",
if (static_cast<int>(i) != -1)
m_imp.print_monomial_with_vars(m_imp.m_monomials[i], tout);
else
tout << "not found";);
return i;
}
};
struct equiv_monomials {
const monomial & m_mon;
const imp& m_imp;
equiv_monomials(const monomial & m, const imp& i) : m_mon(m), m_imp(i) {}
const_iterator_equiv_mon begin() {
return const_iterator_equiv_mon(m_mon.vars(), vars_eq_offsets_begin(), m_imp);
}
const_iterator_equiv_mon end() {
return const_iterator_equiv_mon(m_imp);
}
vector<unsigned_vector::const_iterator> vars_eq_offsets_end() const {
vector<unsigned_vector::const_iterator> r;
for(lpvar j : m_mon.vars()) {
r.push_back(m_imp.abs_eq_vars(j).end());
}
return r;
}
vector<unsigned_vector::const_iterator> vars_eq_offsets_begin() const {
vector<unsigned_vector::const_iterator> r;
for(lpvar j : m_mon.vars()) {
r.push_back(m_imp.abs_eq_vars(j).begin());
}
return r;
}
};
unsigned find_monomial(const unsigned_vector& k) const {
TRACE("nla_solver", tout << "k = "; print_product_with_vars(k, tout););
auto it = m_mkeys.find(k);
if (it == m_mkeys.end()) {
TRACE("nla_solver", tout << "not found";);
return -1;
}
TRACE("nla_solver", tout << "found " << it->second << ", mon = "; print_monomial_with_vars(m_monomials[it->second], tout););
return it->second;
}
const unsigned_vector& abs_eq_vars(lpvar j) const {
rational v = abs(vvr(j));
return m_vars_equivalence.get_vars_with_the_same_abs_val(v);
}
imp(lp::lar_solver& s, reslimit& lim, params_ref const& p)
:
@ -133,7 +255,9 @@ struct solver::imp {
unsigned add(lpvar v, unsigned sz, lpvar const* vs) {
m_monomials.push_back(monomial(v, sz, vs));
TRACE("nla_solver", print_monomial(m_monomials.back(), tout););
return m_monomials.size() - 1;
const auto & m = m_monomials.back();
SASSERT(m_mkeys.find(m.vars()) == m_mkeys.end());
return m_mkeys[m.vars()] = m_monomials.size() - 1;
}
void push() {
@ -149,7 +273,8 @@ struct solver::imp {
}
void deregister_monomial_from_tables(const monomial & m, unsigned i){
m_vars_equivalence.deregister_monomial_from_abs_vals(m, i);
TRACE("nla_solver", tout << "m = "; print_monomial(m, tout););
m_mkeys.erase(m.vars());
deregister_monomial_from_rooted_monomials(m, i);
}
@ -491,14 +616,11 @@ struct solver::imp {
// the value of the i-th monomial has to be equal to the value of the k-th monomial modulo sign
// but it is not the case in the model
void generate_sign_lemma(const vector<rational>& abs_vals, unsigned i, unsigned k, const rational& sign) {
SASSERT(sign == rational(1) || sign == rational(-1));
void generate_sign_lemma(const vector<rational>& abs_vals, const monomial& m, const monomial& n, const rational& sign) {
SASSERT(m_lemma->empty());
TRACE("nla_solver",
tout << "mon i=" << i << " = "; print_monomial_with_vars(m_monomials[i],tout);
tout << '\n';
tout << "mon k=" << k << " = "; print_monomial_with_vars(m_monomials[k],tout);
tout << '\n';
tout << "m = "; print_monomial_with_vars(m, tout);
tout << "n = "; print_monomial_with_vars(n, tout);
tout << "abs_vals="; print_vector(abs_vals, tout);
);
std::unordered_map<rational, vector<index_with_sign>> map;
@ -506,7 +628,7 @@ struct solver::imp {
map[v] = vector<index_with_sign>();
}
for (unsigned j : m_monomials[i]) {
for (unsigned j : m) {
rational v = vvr(j);
int s;
if (v.is_pos()) {
@ -521,7 +643,7 @@ struct solver::imp {
it->second.push_back(index_with_sign(j, rational(s)));
}
for (unsigned j : m_monomials[k]) {
for (unsigned j : n) {
rational v = vvr(j);
rational s;
if (v.is_pos()) {
@ -541,7 +663,7 @@ struct solver::imp {
it->second.pop_back();
}
mk_ineq(m_monomials[i].var(), -sign, m_monomials[k].var(), llc::EQ);
mk_ineq(m.var(), -sign, n.var(), llc::EQ);
TRACE("nla_solver", print_lemma(tout););
}
@ -584,29 +706,58 @@ struct solver::imp {
}
return false;
}
bool basic_sign_lemma_on_a_bucket_of_abs_vals(const vector<rational>& abs_vals, const unsigned_vector& list){
TRACE("nla_solver",
tout << "key = ";
print_vector(abs_vals, tout);
tout << "m0 = ";
print_monomial_with_vars(m_monomials[list[0]], tout);
);
rational val = vvr(m_monomials[list[0]].var());
int sign = vars_sign(m_monomials[list[0]].vars());
if (sign == 0) {
return sign_lemma_for_zero_on_list(list);
}
for (unsigned i = 1; i < list.size(); i++) {
rational rsign = rational(vars_sign(m_monomials[list[i]].vars()) * sign);
SASSERT(!rsign.is_zero());
rational other_val = vvr(m_monomials[list[i]].var());
if (val * rsign != other_val) {
generate_sign_lemma(abs_vals, list[0], list[i], rsign);
return true;
rational rat_sign(const monomial& m) const {
int sign = 1;
for (lpvar j : m) {
auto v = vvr(j);
if (v.is_neg()) {
sign = - sign;
continue;
}
if (v.is_pos()) {
continue;
}
sign = 0;
break;
}
return rational(sign);
}
bool sign_contradiction(const monomial& m, const monomial& n, rational & sign) const {
sign = rat_sign(m) * rat_sign(n);
return vvr(m) != sign * vvr(n) ;
}
vector<rational> abs_vals(const monomial& m) const {
vector<rational> r;
for(unsigned j : m){
r.push_back(abs(vvr(j)));
}
return r;
}
bool basic_sign_lemma_on_two_monomials(const monomial& m, const monomial& n) {
TRACE("nla_solver", tout << "m = "; print_monomial_with_vars(m, tout); tout << "n= "; print_monomial_with_vars(n, tout); );
rational sign;
if (sign_contradiction(m, n, sign)) {
generate_sign_lemma(abs_vals(m) ,m, n, sign);
return true;
}
return false;
}
bool basic_sign_lemma_on_mon(unsigned i){
TRACE("nla_solver", tout << "i = " << i << ", mon = "; print_monomial_with_vars(m_monomials[i], tout););
const monomial& m = m_monomials[i];
for (unsigned n : equiv_monomials(m, *this)) {
if (n == static_cast<unsigned>(-1) || n == i) continue;
if (basic_sign_lemma_on_two_monomials(m_monomials[i], m_monomials[n]))
return true;
}
TRACE("nla_solver",);
return false;
}
@ -626,8 +777,8 @@ struct solver::imp {
-ab = a(-b)
*/
bool basic_sign_lemma() {
for (const auto & p : m_vars_equivalence.monomials_by_abs_values()){
if (basic_sign_lemma_on_a_bucket_of_abs_vals(p.first, p.second))
for (unsigned i : m_to_refine){
if (basic_sign_lemma_on_mon(i))
return true;
}
return false;
@ -998,12 +1149,28 @@ struct solver::imp {
return false;
}
void map_monomial_vars_to_monomial_indices(unsigned i) {
const monomial& m = m_monomials[i];
for (lpvar j : m.vars()) {
auto it = m_monomials_containing_var.find(j);
if (it == m_monomials_containing_var.end()) {
unsigned_vector ms;
ms.push_back(i);
m_monomials_containing_var[j] = ms;
}
else {
it->second.push_back(i);
}
}
}
void map_vars_to_monomials() {
for (unsigned i = 0; i < m_monomials.size(); i++) {
const monomial& m = m_monomials[i];
lpvar v = m.var();
SASSERT(m_var_to_its_monomial.find(v) == m_var_to_its_monomial.end());
m_var_to_its_monomial[v] = i;
map_monomial_vars_to_monomial_indices(i);
}
}
@ -1133,9 +1300,9 @@ struct solver::imp {
void register_monomial_in_tables(unsigned i_mon) {
m_vars_equivalence.register_monomial_in_abs_vals(i_mon, m_monomials[i_mon]);
monomial_coeff mc = canonize_monomial(m_monomials[i_mon]);
TRACE("nla_solver", tout << "mon = ";
const monomial& m = m_monomials[i_mon];
monomial_coeff mc = canonize_monomial(m);
TRACE("nla_solver", tout << "i_mon = " << i_mon << ", mon = ";
print_monomial(m_monomials[i_mon], tout);
tout << "\nmc = ";
print_product(mc.vars(), tout);
@ -1166,6 +1333,7 @@ struct solver::imp {
m_var_to_its_monomial.clear();
m_vars_equivalence.clear();
m_rm_table.clear();
m_monomials_containing_var.clear();
m_expl->clear();
m_lemma->clear();
}
@ -1741,13 +1909,12 @@ struct solver::imp {
return l_undef;
}
bool to_refine = false;
for (unsigned i = 0; i < m_monomials.size() && !to_refine; i++) {
for (unsigned i = 0; i < m_monomials.size(); i++) {
if (!check_monomial(m_monomials[i]))
to_refine = true;
m_to_refine.push_back(i);
}
if (!to_refine) {
if (m_to_refine.empty()) {
return l_true;
}
@ -2277,6 +2444,7 @@ void solver::test_basic_lemma_for_mon_neutral_from_monomial_to_factors() {
}
void solver::test_basic_sign_lemma() {
enable_trace("nla_solver");
lp::lar_solver s;
unsigned a = 0, b = 1, c = 2, d = 3, e = 4,
bde = 7, acd = 8;

View file

@ -41,17 +41,6 @@ struct index_with_sign {
const rational& sign() const { return m_sign; }
};
struct rat_hash {
typedef rational data;
unsigned operator()(const rational& x) const { return x.hash(); }
};
struct hash_vector {
size_t operator()(const vector<rational> & v) const {
return vector_hash<rat_hash>()(v);
}
};
struct vars_equivalence {
@ -82,36 +71,21 @@ struct vars_equivalence {
// if m_tree[v] is not equal to -1 then m_equivs[m_tree[v]] = (k, v) or (v, k), that k is the parent of v
vector<equiv> m_equivs; // all equivalences extracted from constraints
std::unordered_map<rational, unsigned_vector> m_vars_by_abs_values;
std::unordered_map<vector<rational>,
unsigned_vector,
hash_vector> m_monomials_by_abs_vals;
std::function<rational(lpvar)> m_vvr;
// constructor
vars_equivalence(std::function<rational(lpvar)> vvr) : m_vvr(vvr) {}
const std::unordered_map<vector<rational>,
unsigned_vector,
hash_vector>& monomials_by_abs_values() const {
return m_monomials_by_abs_vals;
}
void clear() {
m_equivs.clear();
m_tree.clear();
m_vars_by_abs_values.clear();
m_monomials_by_abs_vals.clear();
m_vars_by_abs_values.clear();
}
svector<lpvar> get_vars_with_the_same_abs_val(const rational& v) const {
svector<unsigned> ret;
const svector<lpvar>& get_vars_with_the_same_abs_val(const rational& v) const {
auto it = m_vars_by_abs_values.find(abs(v));
if (it == m_vars_by_abs_values.end())
return ret;
SASSERT (it != m_vars_by_abs_values.end());
return it->second;
}
@ -242,13 +216,6 @@ struct vars_equivalence {
}
}
void deregister_monomial_from_abs_vals(const monomial & m, unsigned i){
int sign;
auto key = get_sorted_abs_vals_from_mon(m, sign);
SASSERT(m_monomials_by_abs_vals.find(key)->second.back() == i);
m_monomials_by_abs_vals.find(key)->second.pop_back();
}
vector<rational> get_sorted_abs_vals_from_mon(const monomial& m, int & sign) {
sign = 1;
vector<rational> abs_vals;
@ -262,21 +229,5 @@ struct vars_equivalence {
std::sort(abs_vals.begin(), abs_vals.end());
return abs_vals;
}
void register_monomial_in_abs_vals(unsigned i, const monomial & m ) {
int sign;
vector<rational> abs_vals = get_sorted_abs_vals_from_mon(m, sign);
auto it = m_monomials_by_abs_vals.find(abs_vals);
if (it == m_monomials_by_abs_vals.end()) {
unsigned_vector v;
v.push_back(i);
// v is a vector containing a single index_with_sign
m_monomials_by_abs_vals.emplace(abs_vals, v);
}
else {
it->second.push_back(i);
}
}
}; // end of vars_equivalence
}