diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 27c52140f..13957064e 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -59,6 +59,8 @@ Version 4.3.2 - Fixed http://stackoverflow.com/questions/14524316/z3-4-3-get-complete-model. +- Fixed bugs in the C++ API (Thanks to Andrey Kupriyanov). + Version 4.3.1 ============= diff --git a/scripts/mk_unix_dist.py b/scripts/mk_unix_dist.py index 3b9511367..8256236cb 100644 --- a/scripts/mk_unix_dist.py +++ b/scripts/mk_unix_dist.py @@ -190,7 +190,7 @@ def mk_zip(): try: os.chdir(DIST_DIR) zfname = '%s.zip' % dist_path - ZIPOUT = zipfile.ZipFile(zfname, 'w') + ZIPOUT = zipfile.ZipFile(zfname, 'w', zipfile.ZIP_DEFLATED) os.path.walk(dist_path, mk_zip_visitor, '*') if is_verbose(): print "Generated '%s'" % zfname diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 4e1a38af2..873c22b80 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -76,10 +76,7 @@ GPROF=False GIT_HASH=False def check_output(cmd): - try: - return subprocess.check_output(cmd, shell=True).rstrip('\r\n') - except: - return subprocess.check_output(cmd).rstrip('\r\n') + return subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0].rstrip('\r\n') def git_hash(): try: diff --git a/scripts/mk_win_dist.py b/scripts/mk_win_dist.py index 137ea431c..a52c8af43 100644 --- a/scripts/mk_win_dist.py +++ b/scripts/mk_win_dist.py @@ -205,7 +205,7 @@ def mk_zip_core(x64): try: os.chdir(DIST_DIR) zfname = '%s.zip' % dist_path - ZIPOUT = zipfile.ZipFile(zfname, 'w') + ZIPOUT = zipfile.ZipFile(zfname, 'w', zipfile.ZIP_DEFLATED) os.path.walk(dist_path, mk_zip_visitor, '*') if is_verbose(): print "Generated '%s'" % zfname diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 5c1e6ca0c..5af4bf60b 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -619,6 +619,8 @@ namespace z3 { a.check_error(); return expr(a.ctx(), r); } + friend expr implies(expr const & a, bool b) { return implies(a, a.ctx().bool_val(b)); } + friend expr implies(bool a, expr const & b) { return implies(b.ctx().bool_val(a), b); } /** \brief Create the if-then-else expression ite(c, t, e) @@ -758,7 +760,7 @@ namespace z3 { return expr(a.ctx(), r); } friend expr operator-(expr const & a, int b) { return a - a.ctx().num_val(b, a.get_sort()); } - friend expr operator-(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) - a; } + friend expr operator-(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) - b; } friend expr operator<=(expr const & a, expr const & b) { check_context(a, b); @@ -777,7 +779,7 @@ namespace z3 { return expr(a.ctx(), r); } friend expr operator<=(expr const & a, int b) { return a <= a.ctx().num_val(b, a.get_sort()); } - friend expr operator<=(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) <= a; } + friend expr operator<=(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) <= b; } friend expr operator>=(expr const & a, expr const & b) { check_context(a, b); @@ -796,7 +798,7 @@ namespace z3 { return expr(a.ctx(), r); } friend expr operator>=(expr const & a, int b) { return a >= a.ctx().num_val(b, a.get_sort()); } - friend expr operator>=(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) >= a; } + friend expr operator>=(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) >= b; } friend expr operator<(expr const & a, expr const & b) { check_context(a, b); @@ -815,7 +817,7 @@ namespace z3 { return expr(a.ctx(), r); } friend expr operator<(expr const & a, int b) { return a < a.ctx().num_val(b, a.get_sort()); } - friend expr operator<(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) < a; } + friend expr operator<(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) < b; } friend expr operator>(expr const & a, expr const & b) { check_context(a, b); @@ -834,7 +836,7 @@ namespace z3 { return expr(a.ctx(), r); } friend expr operator>(expr const & a, int b) { return a > a.ctx().num_val(b, a.get_sort()); } - friend expr operator>(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) > a; } + friend expr operator>(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) > b; } friend expr operator&(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_bvand(a.ctx(), a, b); return expr(a.ctx(), r); } friend expr operator&(expr const & a, int b) { return a & a.ctx().num_val(b, a.get_sort()); } @@ -888,31 +890,31 @@ namespace z3 { */ inline expr ule(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvule(a.ctx(), a, b)); } inline expr ule(expr const & a, int b) { return ule(a, a.ctx().num_val(b, a.get_sort())); } - inline expr ule(int a, expr const & b) { return ule(b.ctx().num_val(a, b.get_sort()), a); } + inline expr ule(int a, expr const & b) { return ule(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned less than operator for bitvectors. */ inline expr ult(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvult(a.ctx(), a, b)); } inline expr ult(expr const & a, int b) { return ult(a, a.ctx().num_val(b, a.get_sort())); } - inline expr ult(int a, expr const & b) { return ult(b.ctx().num_val(a, b.get_sort()), a); } + inline expr ult(int a, expr const & b) { return ult(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned greater than or equal to operator for bitvectors. */ inline expr uge(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvuge(a.ctx(), a, b)); } inline expr uge(expr const & a, int b) { return uge(a, a.ctx().num_val(b, a.get_sort())); } - inline expr uge(int a, expr const & b) { return uge(b.ctx().num_val(a, b.get_sort()), a); } + inline expr uge(int a, expr const & b) { return uge(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned greater than operator for bitvectors. */ inline expr ugt(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvugt(a.ctx(), a, b)); } inline expr ugt(expr const & a, int b) { return ugt(a, a.ctx().num_val(b, a.get_sort())); } - inline expr ugt(int a, expr const & b) { return ugt(b.ctx().num_val(a, b.get_sort()), a); } + inline expr ugt(int a, expr const & b) { return ugt(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned division operator for bitvectors. */ inline expr udiv(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvudiv(a.ctx(), a, b)); } inline expr udiv(expr const & a, int b) { return udiv(a, a.ctx().num_val(b, a.get_sort())); } - inline expr udiv(int a, expr const & b) { return udiv(b.ctx().num_val(a, b.get_sort()), a); } + inline expr udiv(int a, expr const & b) { return udiv(b.ctx().num_val(a, b.get_sort()), b); } // Basic functions for creating quantified formulas. // The C API should be used for creating quantifiers with patterns, weights, many variables, etc. diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 704ae9fc8..8d643a348 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1849,9 +1849,9 @@ void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * c sort * given = get_sort(args[i]); if (!compatible_sorts(expected, given)) { std::ostringstream buff; - buff << "Invalid function application for " << decl->get_name() << ". "; - buff << "Sort mismatch on argument at position " << (i+1) << ". "; - buff << "Expected: " << mk_pp(expected, m) << " but given " << mk_pp(given, m); + buff << "invalid function application for " << decl->get_name() << ", "; + buff << "sort mismatch on argument at position " << (i+1) << ", "; + buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m); throw ast_exception(buff.str().c_str()); } } @@ -1865,9 +1865,9 @@ void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * c sort * given = get_sort(args[i]); if (!compatible_sorts(expected, given)) { std::ostringstream buff; - buff << "Invalid function application for " << decl->get_name() << ". "; - buff << "Sort mismatch on argument at position " << (i+1) << ". "; - buff << "Expected: " << mk_pp(expected, m) << " but given " << mk_pp(given, m); + buff << "invalid function application for " << decl->get_name() << ", "; + buff << "sort mismatch on argument at position " << (i+1) << ", "; + buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m); throw ast_exception(buff.str().c_str()); } } diff --git a/src/muz_qe/hilbert_basis.cpp b/src/muz_qe/hilbert_basis.cpp index 70bd4b0dd..e89d7807b 100644 --- a/src/muz_qe/hilbert_basis.cpp +++ b/src/muz_qe/hilbert_basis.cpp @@ -21,413 +21,222 @@ Revision History: #include "heap.h" #include "map.h" -typedef u_map offset_refs_t; - template class rational_map : public map {}; -class rational_abs_lt { - vector & m_values; -public: - rational_abs_lt(vector & values): - m_values(values) { - } - bool operator()(int v1, int v2) const { - return m_values[v1] < m_values[v2]; - } -}; - -class hilbert_basis::rational_heap { - vector m_u2r; // [index |-> weight] - rational_map m_r2u; // [weight |-> index] - rational_abs_lt m_lt; // less_than on indices - heap m_heap; // binary heap over weights -public: - - rational_heap(): m_lt(m_u2r), m_heap(10, m_lt) {} - - vector& u2r() { return m_u2r; } - - void insert(unsigned v) { - m_heap.insert(v); - } - - void reset() { - m_u2r.reset(); - m_r2u.reset(); - m_heap.reset(); - } - - bool is_declared(numeral const& r, unsigned& val) const { - return m_r2u.find(r, val); - } - - unsigned declare(numeral const& r) { - SASSERT(!m_r2u.contains(r)); - unsigned val = m_u2r.size(); - m_u2r.push_back(r); - m_r2u.insert(r, val); - m_heap.set_bounds(val+1); - return val; - } - - void find_le(unsigned val, int_vector & result) { - m_heap.find_le(val, result); - } - -}; - -class hilbert_basis::weight_map { +class hilbert_basis::value_index { struct stats { unsigned m_num_comparisons; + unsigned m_num_hit; + unsigned m_num_miss; + stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; - rational_heap m_heap; - vector m_offsets; // [index |-> offset-list] - int_vector m_le; // recycled set of indices with lesser weights - stats m_stats; - svector& m_found; - offset_refs_t& m_refs; - unsigned m_cost; + typedef int_hashtable > int_table; + hilbert_basis& hb; + int_table m_table; + stats m_stats; - unsigned get_value(numeral const& w) { - numeral r = abs(w); - unsigned val; - if (!m_heap.is_declared(r, val)) { - val = m_heap.declare(r); - SASSERT(val == m_offsets.size()); - if (r.is_nonneg()) { - m_heap.insert(val); - } - m_offsets.push_back(unsigned_vector()); - } - return val; - } public: - weight_map(svector& found, offset_refs_t& refs): m_found(found), m_refs(refs) {} - - void insert(offset_t idx, numeral const& w) { - unsigned val = get_value(w); - m_offsets[val].push_back(idx.m_offset); + value_index(hilbert_basis& hb): + hb(hb) + {} + + void insert(offset_t idx, values const& vs) { + m_table.insert(idx.m_offset); } - void remove(offset_t idx, numeral const& w) { - unsigned val = get_value(w); - m_offsets[val].erase(idx.m_offset); - } - - unsigned get_size() const { - unsigned sz = 0; - for (unsigned i = 0; i < m_offsets.size(); ++i) { - sz += m_offsets[i].size(); - } - return sz; + void remove(offset_t idx, values const& vs) { + m_table.remove(idx.m_offset); } void reset() { - m_offsets.reset(); - m_heap.reset(); - m_le.reset(); - } - - unsigned get_cost() const { return m_cost; } - - /** - retrieve - */ - bool init_find(numeral const& w, offset_t idx) { - m_le.reset(); - m_found.reset(); - m_cost = 0; - unsigned val = get_value(w); - // for positive values, the weights should be less or equal. - // for non-positive values, the weights have to be the same. - if (w.is_pos()) { - m_heap.find_le(val, m_le); - } - else { - m_le.push_back(val); - } - for (unsigned i = 0; i < m_le.size(); ++i) { - if (m_heap.u2r()[m_le[i]].is_zero() && !w.is_zero()) { - continue; - } - unsigned_vector const& offsets = m_offsets[m_le[i]]; - for (unsigned j = 0; j < offsets.size(); ++j) { - unsigned offs = offsets[j]; - ++m_stats.m_num_comparisons; - ++m_cost; - if (offs != idx.m_offset) { - m_refs.insert(offs, 0); - m_found.push_back(offset_t(offs)); - } - } - } - return !m_found.empty(); + m_table.reset(); } - unsigned get_find_cost(numeral const& w) { - m_le.reset(); - unsigned cost = 0; - unsigned val = get_value(w); - m_heap.find_le(val, m_le); - for (unsigned i = 0; i < m_le.size(); ++i) { - cost += m_offsets[m_le[i]].size(); - } - return cost; - } - - bool update_find(unsigned round, numeral const& w, offset_t idx) { - m_found.reset(); - m_le.reset(); - m_cost = 0; - unsigned vl = get_value(w); - m_heap.find_le(vl, m_le); - for (unsigned i = 0; i < m_le.size(); ++i) { - unsigned_vector const& offsets = m_offsets[m_le[i]]; - for (unsigned j = 0; j < offsets.size(); ++j) { - unsigned offs = offsets[j]; - ++m_stats.m_num_comparisons; - ++m_cost; - if (offs != idx.m_offset && m_refs.find(offs, vl) && vl == round) { - m_refs.insert(offs, round + 1); - m_found.push_back(offset_t(offs)); - } + bool find(offset_t idx, values const& vs, offset_t& found_idx) { + // display_profile(idx, std::cout); + int_table::iterator it = m_table.begin(), end = m_table.end(); + for (; it != end; ++it) { + offset_t offs(*it); + ++m_stats.m_num_comparisons; + if (*it != idx.m_offset && hb.is_subsumed(idx, offs)) { + found_idx = offs; + ++m_stats.m_num_hit; + return true; } } - return !m_found.empty(); - } + ++m_stats.m_num_miss; + return false; + } void collect_statistics(statistics& st) const { st.update("hb.index.num_comparisons", m_stats.m_num_comparisons); + st.update("hb.index.num_hit", m_stats.m_num_hit); + st.update("hb.index.num_miss", m_stats.m_num_miss); } void reset_statistics() { m_stats.reset(); } -}; - -class hilbert_basis::value_index { - hilbert_basis& hb; - ptr_vector m_values; - offset_refs_t m_refs; - svector m_found; - weight_map m_weight; - -public: - value_index(hilbert_basis& hb): hb(hb) {} - - ~value_index() { - for (unsigned i = 0; i < m_values.size(); ++i) { - dealloc(m_values[i]); - } + unsigned size() const { + return m_table.size(); } - - void init(unsigned num_vars) { - if (m_values.empty()) { - for (unsigned i = 0; i < num_vars; ++i) { - m_values.push_back(alloc(weight_map, m_found, m_refs)); +private: + void display_profile(offset_t idx, std::ostream& out) { + unsigned_vector leq; + unsigned nv = hb.get_num_vars(); + values const& vs = hb.vec(idx); + leq.resize(nv+1); + numeral maxw(0); + for (unsigned i = 0; i < nv; ++i) { + if (!hb.is_abs_geq(maxw, vs[i])) { + maxw = vs[i]; } } - SASSERT(m_values.size() == num_vars); - } - - void insert(offset_t idx, values const& vs) { - for (unsigned i = 0; i < m_values.size(); ++i) { - m_values[i]->insert(idx, vs[i]); + unsigned num_below_max = 0; + int_table::iterator it = m_table.begin(), end = m_table.end(); + for (; it != end; ++it) { + offset_t offs(*it); + values const& ws = hb.vec(offs); + if (ws.weight() <= vs.weight()) { + leq[0]++; + } + bool filtered = false; + for (unsigned i = 0; i < nv; ++i) { + if (hb.is_abs_geq(vs[i], ws[i])) { + leq[i+1]++; + } + if (!hb.is_abs_geq(maxw, ws[i])) { + filtered = true; + } + } + if (!filtered) { + ++num_below_max; + } } - } - - void remove(offset_t idx, values const& vs) { - for (unsigned i = 0; i < m_values.size(); ++i) { - m_values[i]->remove(idx, vs[i]); + out << vs.weight() << ":" << leq[0] << " "; + for (unsigned i = 0; i < nv; ++i) { + out << vs[i] << ":" << leq[i+1] << " "; } + out << " max<= " << num_below_max; + out << "\n"; } }; + + class hilbert_basis::index { - // for each index, a heap of weights. - // for each weight a list of offsets + // for each non-positive weight a separate index. + // for positive weights a shared value index. struct stats { - unsigned m_num_comparisons; unsigned m_num_find; unsigned m_num_insert; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } - }; - - hilbert_basis& hb; - value_index m_values; - rational_map m_negative; - stats m_stats; + }; + + typedef rational_map value_map; + hilbert_basis& hb; + value_map m_neg; + value_index m_pos; + value_index m_zero; + stats m_stats; public: - - index(hilbert_basis& hb): - hb(hb), - m_weight(m_found, m_refs), - m_values(hb) {} - - ~index() { - rational_map::iterator it = m_negative.begin(), end = m_negative.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - } - - void init(unsigned num_vars) { - m_values.init(num_vars); - m_num_vars = num_vars; - SASSERT(m_negative.empty()); - } + index(hilbert_basis& hb): hb(hb), m_pos(hb), m_zero(hb) {} void insert(offset_t idx, values const& vs) { ++m_stats.m_num_insert; - if (vs.value().is_neg()) { - weight_map* w = 0; - if (!m_negative.find(vs.value(), w)) { - w = alloc(weight_map, m_found, m_refs); - } - w->insert(idx, vs); + if (vs.weight().is_pos()) { + m_pos.insert(idx, vs); + } + else if (vs.weight().is_zero()) { + m_zero.insert(idx, vs); } else { - m_weight.insert(idx, vs.value()); - m_values.insert(idx, vs); + value_index* map = 0; + if (!m_neg.find(vs.weight(), map)) { + map = alloc(value_index, hb); + m_neg.insert(vs.weight(), map); + } + map->insert(idx, vs); } } void remove(offset_t idx, values const& vs) { - if (vs.value().is_neg()) { - weight_map* w = m_negative.find(vs.value()); - w->remove(idx, vs); + if (vs.weight().is_pos()) { + m_pos.remove(idx, vs); + } + else if (vs.weight().is_zero()) { + m_zero.remove(idx, vs); } else { - m_weight.remove(idx, vs.value()); - m_values.remove(idx, vs); - } + m_neg.find(vs.weight())->remove(idx, vs); + } } - bool find(values const& vs, offset_t idx, offset_t& found_idx) { + bool find(offset_t idx, values const& vs, offset_t& found_idx) { ++m_stats.m_num_find; - m_refs.reset(); - bool found = m_weight.init_find(vs.value(), idx); - TRACE("hilbert_basis", tout << "init: " << m_found.size() << " cost: " << m_weight.get_cost() << "\n";); -#if 0 - std::cout << vs.value() << " " << m_found.size() << " "; - for (unsigned i = 0; i < m_values.size(); ++i) { - std::cout << vs[i] << ": " << m_values[i]->get_find_cost(vs[i]) << " "; + if (vs.weight().is_pos()) { + return m_pos.find(idx, vs, found_idx); } - std::cout << "\n"; -#endif -#if 0 - for (unsigned i = 0; found && i < m_values.size(); ++i) { - found = m_values[i]->update_find(i, vs[i], idx); - std::cout << vs[i] << ": " << m_found.size() << " "; - TRACE("hilbert_basis", tout << "update " << i << ": " << m_found.size() << " cost: " << m_values[i]->get_cost() << "\n";); + else if (vs.weight().is_zero()) { + return m_zero.find(idx, vs, found_idx); } -#else - for (unsigned i = 0; i < m_found.size(); ++i) { - if (is_subsumed(idx, m_found[i])) { - found_idx = m_found[i]; - return true; - } - } - return false; -#endif - - if (found) { - found_idx = m_found[0]; - } - return found; - } + else { + value_index* map; + return + m_neg.find(vs.weight(), map) && + map->find(idx, vs, found_idx); + } + } void reset() { - for (unsigned i = 0; i < m_values.size(); ++i) { - m_values[i]->reset(); + m_pos.reset(); + m_neg.reset(); + value_map::iterator it = m_neg.begin(), end = m_neg.end(); + for (; it != end; ++it) { + it->m_value->reset(); } - m_weight.reset(); - m_refs.reset(); } void collect_statistics(statistics& st) const { - m_weight.collect_statistics(st); - for (unsigned i = 0; i < m_values.size(); ++i) { - m_values[i]->collect_statistics(st); - } - st.update("hb.index.num_find", m_stats.m_num_find); + m_pos.collect_statistics(st); + m_zero.collect_statistics(st); + value_map::iterator it = m_neg.begin(), end = m_neg.end(); + for (; it != end; ++it) { + it->m_value->collect_statistics(st); + } + st.update("hb.index.num_find", m_stats.m_num_find); st.update("hb.index.num_insert", m_stats.m_num_insert); - st.update("hb.index.size", m_weight.get_size()); + st.update("hb.index.size", size()); } void reset_statistics() { - m_stats.reset(); - m_weight.reset_statistics(); - for (unsigned i = 0; i < m_values.size(); ++i) { - m_values[i]->reset_statistics(); - } - } - - - /** - Vector v is subsumed by vector w if - - v[i] >= w[i] for each index i. - - a*v >= a*w for the evaluation of vectors with respect to a. - - a*v < 0 => a*v = a*w - - - Justification: - - let u := v - w, then - - u[i] >= 0 for each index i - - a*u = a*(v-w) >= 0 - - So v = u + w, where a*u >= 0, a*w >= 0. - - If a*v >= a*w >= 0 then v and w are linear - solutions of e_i, and also v-w is a solution. - - If a*v = a*w < 0, then a*(v-w) = 0, so v can be obtained from w + (v - w). - - */ - - bool is_subsumed(offset_t i, offset_t j) const { - values v = hb.vec(i); - values w = hb.vec(j); - numeral const& n = v.value(); - numeral const& m = w.value(); - bool r = - i.m_offset != j.m_offset && - n >= m && (!m.is_neg() || n == m) && - is_geq(v, w); - CTRACE("hilbert_basis", r, - hb.display(tout, i); - tout << " <= \n"; - hb.display(tout, j); - tout << "\n";); - return r; - } - - bool is_geq(values v, values w) const { - unsigned nv = hb.get_num_vars(); - for (unsigned i = 0; i < nv; ++i) { - if (v[i] < w[i]) { - return false; - } + m_pos.reset_statistics(); + m_zero.reset_statistics(); + value_map::iterator it = m_neg.begin(), end = m_neg.end(); + for (; it != end; ++it) { + it->m_value->reset_statistics(); } - return true; } - - + +private: + unsigned size() const { + unsigned sz = m_pos.size(); + sz += m_zero.size(); + value_map::iterator it = m_neg.begin(), end = m_neg.end(); + for (; it != end; ++it) { + sz += it->m_value->size(); + } + return sz; + } }; /** @@ -435,18 +244,24 @@ public: */ class hilbert_basis::passive { - hilbert_basis& hb; - svector m_passive; - vector m_weights; - unsigned_vector m_free_list; - rational_abs_lt m_lt; // less_than on indices - heap m_heap; // binary heap over weights + struct lt { + passive& p; + lt(passive& p): p(p) {} + bool operator()(int v1, int v2) const { + return p(v1, v2); + } + }; + hilbert_basis& hb; + svector m_passive; + unsigned_vector m_free_list; + lt m_lt; + heap m_heap; // binary heap over weights - numeral get_weight(offset_t idx) { + numeral get_value(offset_t idx) const { numeral w(0); unsigned nv = hb.get_num_vars(); for (unsigned i = 0; i < nv; ++i) { - w += hb.vec(idx)[i]; + w += abs(hb.vec(idx)[i]); } return w; } @@ -455,14 +270,14 @@ public: passive(hilbert_basis& hb): hb(hb) , - m_lt(m_weights), + m_lt(*this), m_heap(10, m_lt) - {} + { + } void reset() { m_heap.reset(); m_free_list.reset(); - m_weights.reset(); m_passive.reset(); } @@ -484,14 +299,12 @@ public: if (m_free_list.empty()) { v = m_passive.size(); m_passive.push_back(idx); - m_weights.push_back(get_weight(idx)); m_heap.set_bounds(v+1); } else { v = m_free_list.back(); m_free_list.pop_back(); m_passive[v] = idx; - m_weights[v] = get_weight(idx); } m_heap.insert(v); } @@ -522,6 +335,43 @@ public: iterator end() { return iterator(*this, m_passive.size()); } + +public: + /** + Prefer positive weights to negative. + If both weights are positive, prefer the smallest weight. + If weights are the same, prefer the one that has smallest sum of values. + */ + bool operator()(int v1, int v2) const { + offset_t idx1 = m_passive[v1]; + offset_t idx2 = m_passive[v2]; + return get_value(idx1) < get_value(idx2); +#if 0 + values const& vec1 = hb.vec(idx1); + values const& vec2 = hb.vec(idx2); + numeral const& w1 = vec1.weight(); + numeral const& w2 = vec2.weight(); + SASSERT(!w1.is_zero()); + SASSERT(!w2.is_zero()); + + if (w1.is_pos()) { + if (w2.is_neg()) { + return true; + } + if (w1 != w2) { + return w1 < w2; + } + } + else { + if (w2.is_pos()) { + return false; + } + } + SASSERT(w1 == w2); + return get_value(idx1) < get_value(idx2); +#endif + } + }; hilbert_basis::hilbert_basis(): @@ -573,10 +423,8 @@ void hilbert_basis::add_ge(num_vector const& v, numeral const& b) { num_vector w; w.push_back(-b); w.append(v); - if (m_ineqs.empty()) { - m_index->init(w.size()); - } m_ineqs.push_back(w); + m_iseq.push_back(false); } void hilbert_basis::add_le(num_vector const& v, numeral const& b) { @@ -588,8 +436,12 @@ void hilbert_basis::add_le(num_vector const& v, numeral const& b) { } void hilbert_basis::add_eq(num_vector const& v, numeral const& b) { - add_le(v, b); - add_ge(v, b); + SASSERT(m_ineqs.empty() || v.size() + 1 == get_num_vars()); + num_vector w; + w.push_back(-b); + w.append(v); + m_ineqs.push_back(w); + m_iseq.push_back(true); } void hilbert_basis::add_ge(num_vector const& v) { @@ -601,8 +453,15 @@ void hilbert_basis::add_le(num_vector const& v) { } void hilbert_basis::add_eq(num_vector const& v) { - add_le(v); - add_ge(v); + add_eq(v, numeral(0)); +} + +void hilbert_basis::set_is_int(unsigned var_index) { + // + // The 0't index is reserved for the constant + // coefficient. Shift indices by 1. + // + m_ints.push_back(var_index+1); } unsigned hilbert_basis::get_num_vars() const { @@ -624,26 +483,36 @@ void hilbert_basis::init_basis() { m_free_list.reset(); unsigned num_vars = get_num_vars(); for (unsigned i = 0; i < num_vars; ++i) { - num_vector w(num_vars, numeral(0)); - w[i] = numeral(1); - offset_t idx = alloc_vector(); - values v = vec(idx); - for (unsigned i = 0; i < num_vars; ++i) { - v[i] = w[i]; - } - m_basis.push_back(idx); + add_unit_vector(i, numeral(1)); } + for (unsigned i = 0; i < m_ints.size(); ++i) { + add_unit_vector(m_ints[i], numeral(-1)); + } +} + +void hilbert_basis::add_unit_vector(unsigned i, numeral const& e) { + unsigned num_vars = get_num_vars(); + num_vector w(num_vars, numeral(0)); + w[i] = e; + offset_t idx = alloc_vector(); + values v = vec(idx); + for (unsigned j = 0; j < num_vars; ++j) { + v[j] = w[j]; + } + m_basis.push_back(idx); } lbool hilbert_basis::saturate() { - init_basis(); - for (unsigned i = 0; !m_cancel && i < m_ineqs.size(); ++i) { - lbool r = saturate(m_ineqs[i]); + init_basis(); + m_current_ineq = 0; + while (!m_cancel && m_current_ineq < m_ineqs.size()) { + select_inequality(); + lbool r = saturate(m_ineqs[m_current_ineq], m_iseq[m_current_ineq]); ++m_stats.m_num_saturations; if (r != l_true) { return r; - } - + } + ++m_current_ineq; } if (m_cancel) { return l_undef; @@ -651,19 +520,19 @@ lbool hilbert_basis::saturate() { return l_true; } -lbool hilbert_basis::saturate(num_vector const& ineq) { +lbool hilbert_basis::saturate(num_vector const& ineq, bool is_eq) { m_active.reset(); m_passive->reset(); m_zero.reset(); m_index->reset(); - TRACE("hilbert_basis", display_ineq(tout, ineq);); + TRACE("hilbert_basis", display_ineq(tout, ineq, is_eq);); bool has_non_negative = false; iterator it = begin(); for (; it != end(); ++it) { values v = vec(*it); - set_eval(v, ineq); + v.weight() = get_weight(v, ineq); add_goal(*it); - if (v.value().is_nonneg()) { + if (v.weight().is_nonneg()) { has_non_negative = true; } } @@ -694,9 +563,9 @@ lbool hilbert_basis::saturate(num_vector const& ineq) { // Move positive from active and zeros to new basis. m_basis.reset(); m_basis.append(m_zero); - for (unsigned i = 0; i < m_active.size(); ++i) { + for (unsigned i = 0; !is_eq && i < m_active.size(); ++i) { offset_t idx = m_active[i]; - if (vec(idx).value().is_pos()) { + if (vec(idx).weight().is_pos()) { m_basis.push_back(idx); } else { @@ -710,6 +579,52 @@ lbool hilbert_basis::saturate(num_vector const& ineq) { return l_true; } +void hilbert_basis::select_inequality() { + SASSERT(m_current_ineq < m_ineqs.size()); + unsigned best = m_current_ineq; + unsigned non_zeros = get_num_nonzeros(m_ineqs[best]); + unsigned prod = get_ineq_product(m_ineqs[best]); + for (unsigned j = best+1; prod != 0 && j < m_ineqs.size(); ++j) { + unsigned non_zeros2 = get_num_nonzeros(m_ineqs[j]); + unsigned prod2 = get_ineq_product(m_ineqs[j]); + if (prod2 < prod || (prod2 == prod && non_zeros2 < non_zeros)) { + prod = prod2; + non_zeros = non_zeros2; + best = j; + } + } + if (best != m_current_ineq) { + std::swap(m_ineqs[m_current_ineq], m_ineqs[best]); + std::swap(m_iseq[m_current_ineq], m_iseq[best]); + } +} + +unsigned hilbert_basis::get_num_nonzeros(num_vector const& ineq) { + unsigned count = 0; + for (unsigned i = 0; i < ineq.size(); ++i) { + if (!ineq[i].is_zero()) { + ++count; + } + } + return count; +} + +unsigned hilbert_basis::get_ineq_product(num_vector const& ineq) { + unsigned num_pos = 0, num_neg = 0; + iterator it = begin(); + for (; it != end(); ++it) { + values v = vec(*it); + numeral w = get_weight(v, ineq); + if (w.is_pos()) { + ++num_pos; + } + else if (w.is_neg()) { + ++num_neg; + } + } + return num_pos * num_neg; +} + void hilbert_basis::recycle(offset_t idx) { m_index->remove(idx, vec(idx)); m_free_list.push_back(idx); @@ -724,8 +639,8 @@ void hilbert_basis::resolve(offset_t i, offset_t j, offset_t r) { for (unsigned k = 0; k < nv; ++k) { u[k] = v[k] + w[k]; } - u.value() = v.value() + w.value(); - // std::cout << "resolve: " << v.value() << " + " << w.value() << " = " << u.value() << "\n"; + u.weight() = v.weight() + w.weight(); + // std::cout << "resolve: " << v.weight() << " + " << w.weight() << " = " << u.weight() << "\n"; TRACE("hilbert_basis_verbose", display(tout, i); display(tout, j); @@ -738,7 +653,7 @@ hilbert_basis::offset_t hilbert_basis::alloc_vector() { if (m_free_list.empty()) { unsigned num_vars = get_num_vars(); unsigned idx = m_store.size(); - m_store.resize(idx + 1 + get_num_vars()); + m_store.resize(idx + 1 + num_vars); return offset_t(idx); } else { @@ -754,7 +669,7 @@ void hilbert_basis::add_goal(offset_t idx) { return; } m_index->insert(idx, v); - if (v.value().is_zero()) { + if (v.weight().is_zero()) { m_zero.push_back(idx); } else { @@ -765,12 +680,7 @@ void hilbert_basis::add_goal(offset_t idx) { bool hilbert_basis::is_subsumed(offset_t idx) { offset_t found_idx; - if (m_index->find(vec(idx), idx, found_idx)) { - TRACE("hilbert_basis", - display(tout, idx); - tout << " <= \n"; - display(tout, found_idx); - tout << "\n";); + if (m_index->find(idx, vec(idx), found_idx)) { ++m_stats.m_num_subsumptions; return true; } @@ -778,13 +688,30 @@ bool hilbert_basis::is_subsumed(offset_t idx) { } bool hilbert_basis::can_resolve(offset_t i, offset_t j) const { - sign_t s1 = get_sign(i); - sign_t s2 = get_sign(j); - return s1 != s2 && abs(vec(i)[0] + vec(j)[0]) <= numeral(1); + if (get_sign(i) == get_sign(j)) { + return false; + } + values const& v1 = vec(i); + values const& v2 = vec(j); + // index 0 is reserved for the constant coefficient. + // The value of it should either be 0 or 1. + if (abs(v1[0] + v2[0]) > numeral(1)) { + return false; + } + for (unsigned i = 0; i < m_ints.size(); ++i) { + unsigned j = m_ints[i]; + if (v1[j].is_pos() && v2[j].is_neg()) { + return false; + } + if (v1[j].is_neg() && v2[j].is_pos()) { + return false; + } + } + return true; } hilbert_basis::sign_t hilbert_basis::get_sign(offset_t idx) const { - numeral val = vec(idx).value(); + numeral val = vec(idx).weight(); if (val.is_pos()) { return pos; } @@ -794,20 +721,20 @@ hilbert_basis::sign_t hilbert_basis::get_sign(offset_t idx) const { return zero; } -void hilbert_basis::set_eval(values& val, num_vector const& ineq) const { +hilbert_basis::numeral hilbert_basis::get_weight(values& val, num_vector const& ineq) const { numeral result(0); unsigned num_vars = get_num_vars(); for (unsigned i = 0; i < num_vars; ++i) { result += val[i]*ineq[i]; } - val.value() = result; + return result; } void hilbert_basis::display(std::ostream& out) const { unsigned nv = get_num_vars(); out << "inequalities:\n"; for (unsigned i = 0; i < m_ineqs.size(); ++i) { - display_ineq(out, m_ineqs[i]); + display_ineq(out, m_ineqs[i], m_iseq[i]); } if (!m_basis.empty()) { out << "basis:\n"; @@ -835,12 +762,11 @@ void hilbert_basis::display(std::ostream& out) const { display(out, m_zero[i]); } } - } void hilbert_basis::display(std::ostream& out, offset_t o) const { display(out, vec(o)); - out << " -> " << vec(o).value() << "\n"; + out << " -> " << vec(o).weight() << "\n"; } void hilbert_basis::display(std::ostream& out, values const& v) const { @@ -850,7 +776,7 @@ void hilbert_basis::display(std::ostream& out, values const& v) const { } } -void hilbert_basis::display_ineq(std::ostream& out, num_vector const& v) const { +void hilbert_basis::display_ineq(std::ostream& out, num_vector const& v, bool is_eq) const { unsigned nv = get_num_vars(); for (unsigned j = 0; j < nv; ++j) { if (!v[j].is_zero()) { @@ -871,7 +797,12 @@ void hilbert_basis::display_ineq(std::ostream& out, num_vector const& v) const { out << "x" << j; } } - out << " >= 0\n"; + if (is_eq) { + out << " = 0\n"; + } + else { + out << " >= 0\n"; + } } @@ -886,3 +817,71 @@ void hilbert_isl_basis::add_le(num_vector const& v, numeral bound) { } m_basis.add_le(w); } + + +/** + Vector v is subsumed by vector w if + + v[i] >= w[i] for each index i. + + a*v >= a*w for the evaluation of vectors with respect to a. + + . a*v < 0 => a*v = a*w + . a*v > 0 => a*w > 0 + . a*v = 0 => a*w = 0 + + Justification: + + let u := v - w, then + + u[i] >= 0 for each index i + + a*u = a*(v-w) >= 0 + + So v = u + w, where a*u >= 0, a*w >= 0. + + If a*v >= a*w >= 0 then v and w are linear + solutions of e_i, and also v-w is a solution. + + If a*v = a*w < 0, then a*(v-w) = 0, so v can be obtained from w + (v - w). + +*/ + +bool hilbert_basis::is_subsumed(offset_t i, offset_t j) const { + values v = vec(i); + values w = vec(j); + numeral const& n = v.weight(); + numeral const& m = w.weight(); + bool r = + i.m_offset != j.m_offset && + n >= m && (!m.is_nonpos() || n == m) && + is_geq(v, w); + for (unsigned k = 0; r && k < m_current_ineq; ++k) { + r = get_weight(vec(i), m_ineqs[k]) >= get_weight(vec(j), m_ineqs[k]); + } + CTRACE("hilbert_basis", r, + display(tout, i); + tout << " <= \n"; + display(tout, j); + tout << "\n";); + return r; +} + +bool hilbert_basis::is_geq(values const& v, values const& w) const { + unsigned nv = get_num_vars(); + for (unsigned i = 0; i < nv; ++i) { + if (!is_abs_geq(v[i], w[i])) { + return false; + } + } + return true; +} + +bool hilbert_basis::is_abs_geq(numeral const& v, numeral const& w) const { + if (w.is_neg()) { + return v <= w; + } + else { + return v >= w; + } +} diff --git a/src/muz_qe/hilbert_basis.h b/src/muz_qe/hilbert_basis.h index 4e3b76542..be136fbf9 100644 --- a/src/muz_qe/hilbert_basis.h +++ b/src/muz_qe/hilbert_basis.h @@ -34,11 +34,9 @@ public: typedef rational numeral; typedef vector num_vector; private: - class rational_heap; class value_index; class index; class passive; - class weight_map; struct offset_t { unsigned m_offset; offset_t(unsigned o) : m_offset(o) {} @@ -59,25 +57,28 @@ private: numeral* m_values; public: values(numeral* v):m_values(v) {} - numeral& value() { return m_values[0]; } // value of a*x + numeral& weight() { return m_values[0]; } // value of a*x numeral& operator[](unsigned i) { return m_values[i+1]; } // value of x_i - numeral const& value() const { return m_values[0]; } // value of a*x + numeral const& weight() const { return m_values[0]; } // value of a*x numeral const& operator[](unsigned i) const { return m_values[i+1]; } // value of x_i }; - vector m_ineqs; - num_vector m_store; - svector m_basis; - svector m_free_list; - svector m_active; - svector m_zero; - volatile bool m_cancel; + vector m_ineqs; // set of asserted inequalities + svector m_iseq; // inequalities that are equalities + num_vector m_store; // store of vectors + svector m_basis; // vector of current basis + svector m_free_list; // free list of unused storage + svector m_active; // active set + svector m_zero; // zeros + passive* m_passive; // passive set + volatile bool m_cancel; stats m_stats; - index* m_index; - passive* m_passive; + index* m_index; // index of generated vectors + unsigned_vector m_ints; // indices that can be both positive and negative + unsigned m_current_ineq; class iterator { hilbert_basis const& hb; - unsigned m_idx; + unsigned m_idx; public: iterator(hilbert_basis const& hb, unsigned idx): hb(hb), m_idx(idx) {} offset_t operator*() const { return hb.m_basis[m_idx]; } @@ -89,10 +90,17 @@ private: static offset_t mk_invalid_offset(); static bool is_invalid_offset(offset_t offs); - lbool saturate(num_vector const& ineq); + lbool saturate(num_vector const& ineq, bool is_eq); void init_basis(); + void select_inequality(); + unsigned get_num_nonzeros(num_vector const& ineq); + unsigned get_ineq_product(num_vector const& ineq); + + void add_unit_vector(unsigned i, numeral const& e); unsigned get_num_vars() const; - void set_eval(values& val, num_vector const& ineq) const; + numeral get_weight(values& val, num_vector const& ineq) const; + bool is_geq(values const& v, values const& w) const; + bool is_abs_geq(numeral const& v, numeral const& w) const; bool is_subsumed(offset_t idx); bool is_subsumed(offset_t i, offset_t j) const; void recycle(offset_t idx); @@ -108,7 +116,7 @@ private: void display(std::ostream& out, offset_t o) const; void display(std::ostream& out, values const & v) const; - void display_ineq(std::ostream& out, num_vector const& v) const; + void display_ineq(std::ostream& out, num_vector const& v, bool is_eq) const; public: @@ -131,6 +139,8 @@ public: void add_le(num_vector const& v, numeral const& b); void add_eq(num_vector const& v, numeral const& b); + void set_is_int(unsigned var_index); + lbool saturate(); void set_cancel(bool f) { m_cancel = f; } diff --git a/src/test/hilbert_basis.cpp b/src/test/hilbert_basis.cpp index 86b0f1bad..4d401c4c2 100644 --- a/src/test/hilbert_basis.cpp +++ b/src/test/hilbert_basis.cpp @@ -74,6 +74,14 @@ static void gorrila_test(unsigned seed, unsigned n, unsigned k, unsigned bound, saturate_basis(hb); } +static vector vec(int i, int j) { + vector nv; + nv.resize(2); + nv[0] = rational(i); + nv[1] = rational(j); + return nv; +} + static vector vec(int i, int j, int k) { vector nv; nv.resize(3); @@ -243,25 +251,37 @@ static void tst11() { saturate_basis(hb); } +static void tst12() { + hilbert_basis hb; + hb.add_le(vec(1, 0), R(1)); + hb.add_le(vec(0, 1), R(1)); + saturate_basis(hb); +} + void tst_hilbert_basis() { std::cout << "hilbert basis test\n"; -#if 0 - tst1(); - tst2(); - tst3(); - tst4(); - tst5(); - tst6(); - tst7(); - tst8(); - tst9(); - tst10(); - tst11(); - gorrila_test(0, 4, 3, 20, 5); - gorrila_test(1, 4, 3, 20, 5); - gorrila_test(2, 4, 3, 20, 5); - gorrila_test(0, 4, 2, 20, 5); - gorrila_test(0, 4, 2, 20, 5); -#endif - gorrila_test(0, 10, 7, 20, 11); + tst12(); + return; + + if (true) { + tst1(); + tst2(); + tst3(); + tst4(); + tst5(); + tst6(); + tst7(); + tst8(); + tst9(); + tst10(); + tst11(); + gorrila_test(0, 4, 3, 20, 5); + gorrila_test(1, 4, 3, 20, 5); + gorrila_test(2, 4, 3, 20, 5); + gorrila_test(0, 4, 2, 20, 5); + gorrila_test(0, 4, 2, 20, 5); + } + else { + gorrila_test(0, 10, 7, 20, 11); + } }