From 1dca8abc05ad93e19d3987d0757fb4b692ae83f4 Mon Sep 17 00:00:00 2001 From: Lev Date: Fri, 1 Feb 2019 19:42:14 -0800 Subject: [PATCH] model based sign lemma Signed-off-by: Lev --- src/util/lp/nla_solver.cpp | 259 +++++++++++++++++++++++---------- src/util/lp/vars_equivalence.h | 7 +- 2 files changed, 188 insertions(+), 78 deletions(-) diff --git a/src/util/lp/nla_solver.cpp b/src/util/lp/nla_solver.cpp index c5f001d1d..3e9e02c87 100644 --- a/src/util/lp/nla_solver.cpp +++ b/src/util/lp/nla_solver.cpp @@ -26,6 +26,15 @@ #include "util/lp/rooted_mons.h" namespace nla { +template +bool try_insert(const A& elem, B& collection) { + auto it = collection.find(elem); + if (it != collection.end()) + return false; + collection.insert(elem); + return true; +} + typedef lp::lconstraint_kind llc; struct point { rational x; @@ -47,6 +56,21 @@ point operator-(const point& a, const point& b) { return point(a.x - b.x, a.y - b.y); } +void divide_by_common_factor(unsigned_vector& a, unsigned_vector& b) { + SASSERT(lp::is_non_decreasing(a) && lp::is_non_decreasing(b)); + unsigned i = 0, j = 0; + while (i < a.size() && j < b.size()){ + if (a[i] == b[j]) { + a.erase(a.begin() + i); + b.erase(b.begin() + j); + } else if (a[i] < b[j]) { + i++; + } else { + j++; + } + } +} + struct solver::imp { struct bfc { lpvar m_x, m_y; @@ -88,11 +112,6 @@ struct solver::imp { 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) : m_vars_equivalence([this](unsigned h){return vvr(h);}), @@ -563,6 +582,51 @@ struct solver::imp { TRACE("nla_solver", print_lemma(current_lemma(), tout);); } + // 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 + // abs_vars_key is formed by m_vars_equivalence.get_abs_root_for_var(k), where + // k runs over m. + void generate_sign_lemma_model_based(const monomial& m, const monomial& n, const rational& sign) { + add_empty_lemma_and_explanation(); + TRACE("nla_solver", + ); + unsigned_vector mvars(m.vars()); + unsigned_vector nvars(n.vars()); + divide_by_common_factor(mvars, nvars); + TRACE("nla_solver", + tout << "m = "; print_monomial_with_vars(m, tout); + tout << "n = "; print_monomial_with_vars(n, tout); + tout << "mvars = "; print_product(mvars, tout); + tout << "\nnvars = "; print_product(nvars, tout); + ); + std::unordered_map map; + const unsigned_vector key = get_abs_key(mvars); + + // create and fill the map from "abs root vars" to lists, + // all elementl of map[j] have the same abs vvr() as vvr(j) + for (lpvar j : key) { + map[j] = unsigned_vector(); + } + + for (unsigned j : mvars) { + unsigned k = m_vars_equivalence.get_abs_root_for_var(abs(vvr(j))); + map.find(k)->second.push_back(j); + } + + for (unsigned j : nvars) { + unsigned k = m_vars_equivalence.get_abs_root_for_var(abs(vvr(j))); + auto it = map.find(k); + lpvar jj = it->second.back(); + rational s = vvr(jj) == vvr(j)? rational(1) : rational(-1); + // todo : check that each pair is processed only once + mk_ineq(j, -s, jj, llc::NE, current_lemma()); + it->second.pop_back(); + } + + mk_ineq(m.var(), -sign, n.var(), llc::EQ, current_lemma()); + TRACE("nla_solver", print_lemma(current_lemma(), tout);); + } + lemma& current_lemma() {return m_lemma_vec->back();} lp::explanation& current_expl() {return m_expl_vec->back();} @@ -645,11 +709,74 @@ struct solver::imp { return false; } - bool basic_sign_lemma_on_mon(unsigned i){ + void init_abs_val_table() { + // register only those that a factors in a monomial + for (auto & p : m_monomials_containing_var) { + TRACE("nla_solver", tout << "p.first = " << p.first << std::endl;); + m_vars_equivalence.register_var_with_abs_val(p.first, vvr(p.first)); + } + } + + template + unsigned_vector get_abs_key(const T& m) const { + unsigned_vector r; + for (unsigned j : m) { + r.push_back(m_vars_equivalence.get_abs_root_for_var(abs(vvr(j)))); + } + std::sort(r.begin(), r.end()); + return r; + } + + // bool basic_sign_lemma_on_mon_model_based(unsigned i_mon, std::unordered_map& key_mons) { + // const monomial& m = m_monomials[i_mon]; + // unsigned_vector abs_key = get_abs_key(m); + // } + + std::unordered_map create_relevant_abs_keys() { + std::unordered_map r; + for (unsigned i : m_to_refine) { + unsigned_vector key = get_abs_key(m_monomials[i]); + if (contains(r, key)) + continue; + r[key] = i; + } + return r; + } + + bool basic_sign_lemma_on_two_mons_model_based(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_model_based(m, n, sign); + return true; + } + return false; + } + bool basic_sign_lemma_model_based() { + init_abs_val_table(); + std::unordered_map key_mons = + create_relevant_abs_keys(); + for (unsigned i = 0; i < m_monomials.size(); i++) { + unsigned_vector key = get_abs_key(m_monomials[i]); + auto it = key_mons.find(key); + if (it == key_mons.end() || it->second == i) + continue; + if (basic_sign_lemma_on_two_mons_model_based(m_monomials[it->second], m_monomials[i])) + return true; + } + return false; + } + + + bool basic_sign_lemma_on_mon(unsigned i, std::unordered_set & explored){ const monomial& m = m_monomials[i]; TRACE("nla_solver", tout << "i = " << i << ", mon = "; print_monomial_with_vars(m, tout);); const index_with_sign& rm_i_s = m_rm_table.get_rooted_mon(i); - const auto& mons_to_explore = m_rm_table.vec()[rm_i_s.index()].m_mons; + unsigned k = rm_i_s.index(); + if (!try_insert(k, explored)) + return false; + + const auto& mons_to_explore = m_rm_table.vec()[k].m_mons; for (index_with_sign i_s : mons_to_explore) { unsigned n = i_s.index(); @@ -665,10 +792,13 @@ struct solver::imp { * \brief b && c > 0 => ac > bc // ac is a factorization of rm.vars() // ac[k] plays the role of c - bool order_lemma_on_factor(const rooted_mon& rm, const factorization& ac, unsigned k, bool model_based) { + bool order_lemma_on_factor(const rooted_mon& rm, const factorization& ac, unsigned k, bool derived) { auto c = ac[k]; TRACE("nla_solver", tout << "k = " << k << ", c = "; print_factor_with_vars(c, tout); ); - for (const factor & d : factors_with_the_same_abs_val(c, model_based)) { - if (order_lemma_on_ad(rm, ac, k, model_based, d)) + for (const factor & d : factors_with_the_same_abs_val(c, derived)) { + if (order_lemma_on_ad(rm, ac, k, derived, d)) return true; } return false; @@ -1554,7 +1655,7 @@ struct solver::imp { // a > b && c == d => ac > bd // ac is a factorization of rm.vars() - bool order_lemma_on_factorization(const rooted_mon& rm, const factorization& ac, bool model_based) { + bool order_lemma_on_factorization(const rooted_mon& rm, const factorization& ac, bool derived) { SASSERT(ac.size() == 2); CTRACE("nla_solver", rm.vars().size() == 4, @@ -1565,7 +1666,7 @@ struct solver::imp { if (v.is_zero()) continue; - if (order_lemma_on_factor(rm, ac, k, model_based)) { + if (order_lemma_on_factor(rm, ac, k, derived)) { return true; } } @@ -1573,19 +1674,19 @@ struct solver::imp { } // a > b && c > 0 => ac > bc - bool order_lemma_on_monomial(const rooted_mon& rm, bool model_based) { + bool order_lemma_on_monomial(const rooted_mon& rm, bool derived) { TRACE("nla_solver", tout << "rm = "; print_product(rm, tout); tout << ", orig = "; print_monomial(m_monomials[rm.orig_index()], tout); ); for (auto ac : factorization_factory_imp(rm.vars(), *this)) { - if (ac.size() == 2 && order_lemma_on_factorization(rm, ac, model_based)) + if (ac.size() == 2 && order_lemma_on_factorization(rm, ac, derived)) return true; } return false; } - bool order_lemma(bool model_based) { + bool order_lemma(bool derived) { TRACE("nla_solver", ); const auto& rm_ref = m_rm_table.to_refine(); @@ -1593,7 +1694,7 @@ struct solver::imp { unsigned i = start; do { const rooted_mon& rm = m_rm_table.vec()[rm_ref[i]]; - if (order_lemma_on_monomial(rm, model_based)) { + if (order_lemma_on_monomial(rm, derived)) { return true; } if (++i == rm_ref.size()) { @@ -1748,6 +1849,7 @@ struct solver::imp { assert_abs_val_a_le_abs_var_b(a, b, true); explain(a, current_expl()); explain(b, current_expl()); + TRACE("nla_solver", print_lemma_and_expl(tout);); } // not a strict version @@ -1763,6 +1865,7 @@ struct solver::imp { assert_abs_val_a_le_abs_var_b(a, b, false); explain(a, current_expl()); explain(b, current_expl()); + TRACE("nla_solver", print_lemma_and_expl(tout);); } bool monotonicity_lemma_on_lex_sorted_rm_upper(const vector, unsigned>>& lex_sorted, unsigned i, const rooted_mon& rm) { @@ -2070,8 +2173,8 @@ struct solver::imp { if (order_lemma(derived)) { ret = l_false; } - } else { // search_level == 3 - if (monotonicity_lemma() || tangent_lemma()) { + } else { // search_level == 2 + if (!derived && (monotonicity_lemma() || tangent_lemma())) { ret = l_false; } } @@ -2098,7 +2201,8 @@ struct solver::imp { lbool ret = inner_check(true); if (ret == l_undef) ret = inner_check(false); - + + TRACE("nla_solver", tout << "ret = " << ret;); IF_VERBOSE(2, if(ret == l_undef) {verbose_stream() << "Monomials\n"; print_monomials(verbose_stream());}); CTRACE("nla_solver", ret == l_undef, tout << "Monomials\n"; print_monomials(tout);); SASSERT(ret != l_false || no_lemmas_hold()); @@ -2396,6 +2500,7 @@ void solver::test_basic_lemma_for_mon_neutral_from_factors_to_monomial_1() { void solver::test_basic_lemma_for_mon_zero_from_factors_to_monomial() { std::cout << "test_basic_lemma_for_mon_zero_from_factors_to_monomial\n"; + enable_trace("nla_solver"); lp::lar_solver s; unsigned a = 0, b = 1, c = 2, d = 3, e = 4, abcde = 5, ac = 6, bde = 7, acd = 8, be = 9; @@ -2442,7 +2547,7 @@ void solver::test_basic_lemma_for_mon_zero_from_factors_to_monomial() { s.set_column_value(lp_be, rational(1)); SASSERT(nla.m_imp->test_check(lemma, exp) == l_false); - nla.m_imp->print_lemma(lemma.back(), std::cout << "expl & lemma: "); + nla.m_imp->print_lemma_and_expl(std::cout); SASSERT(lemma.size() == 1 && lemma[0].size() == 2); ineq i0(llc::NE, lp::lar_term(), rational(0)); i0.m_term.add_var(lp_b); @@ -2615,13 +2720,13 @@ void solver::test_basic_sign_lemma() { lp::lar_solver s; unsigned a = 0, b = 1, c = 2, d = 3, e = 4, bde = 7, acd = 8; - lpvar lp_a = s.add_var(a, true); - lpvar lp_b = s.add_var(b, true); - lpvar lp_c = s.add_var(c, true); - lpvar lp_d = s.add_var(d, true); - lpvar lp_e = s.add_var(e, true); - lpvar lp_bde = s.add_var(bde, true); - lpvar lp_acd = s.add_var(acd, true); + lpvar lp_a = s.add_named_var(a, true, "a"); + lpvar lp_b = s.add_named_var(b, true, "b"); + lpvar lp_c = s.add_named_var(c, true, "c"); + lpvar lp_d = s.add_named_var(d, true, "d"); + lpvar lp_e = s.add_named_var(e, true, "e"); + lpvar lp_bde = s.add_named_var(bde, true, "bde"); + lpvar lp_acd = s.add_named_var(acd, true, "acd"); reslimit l; params_ref p; @@ -2668,7 +2773,7 @@ void solver::test_basic_sign_lemma() { t.add_var(lp_acd); ineq q(llc::EQ, t, rational(0)); - nla.m_imp->print_lemma(lemma.back(), std::cout << "expl & lemma: "); + nla.m_imp->print_lemma_and_expl(std::cout); SASSERT(q == lemma[0].back()); ineq i0(llc::EQ, lp::lar_term(), rational(0)); i0.m_term.add_var(lp_bde); diff --git a/src/util/lp/vars_equivalence.h b/src/util/lp/vars_equivalence.h index c70cd0880..18a8e8274 100644 --- a/src/util/lp/vars_equivalence.h +++ b/src/util/lp/vars_equivalence.h @@ -254,7 +254,12 @@ struct vars_equivalence { explain(j, exp); } - void register_var(unsigned j, const rational& val) { + unsigned get_abs_root_for_var(const rational & v) const { + SASSERT(!v.is_neg()); + return *(m_vars_by_abs_values.find(v)->second.begin()); + } + + void register_var_with_abs_val(unsigned j, const rational& val) { TRACE("nla_vars_eq", tout << "j = " << j;); rational v = abs(val); auto it = m_vars_by_abs_values.find(v);