diff --git a/src/math/polysat/forbidden_intervals.cpp b/src/math/polysat/forbidden_intervals.cpp index b4fa8f61b..0cfb571b9 100644 --- a/src/math/polysat/forbidden_intervals.cpp +++ b/src/math/polysat/forbidden_intervals.cpp @@ -151,9 +151,6 @@ namespace polysat { auto next_hi = records[next_i].interval.hi(); auto lhs = hi - next_lo; auto rhs = next_hi - next_lo; - // NB: do we really have to pass in the level to this new literal? - // seems separating the level from the constraint is what we want - // the level of a literal is when it was assigned. Lemmas could have unassigned literals. signed_constraint c = s.m_constraints.ult(lhs, rhs); LOG("constraint: " << c); core.insert(c); @@ -195,169 +192,76 @@ namespace polysat { }; backtrack _backtrack(out_side_cond); - - // Current only works when degree(v) is at most one on both sides - pdd lhs = c->to_ule().lhs(); - pdd rhs = c->to_ule().rhs(); - unsigned const deg1 = lhs.degree(v); - unsigned const deg2 = rhs.degree(v); - if (deg1 > 1 || deg2 > 1) - return false; - - if (deg1 == 0 && deg2 == 0) { - return false; - UNREACHABLE(); // this case is not useful for conflict resolution (but it could be handled in principle) - // i is empty or full, condition would be this constraint itself? - return true; - } - - unsigned const sz = s.size(v); - dd::pdd_manager& m = s.sz2pdd(sz); - rational const pow2 = rational::power_of_two(sz); - rational const minus_one = pow2 - 1; - + /** * TODO: to express the interval for coefficient 2^i symbolically, * we need right-shift/upper-bits-extract in the language. * So currently we can only do it if the coefficient is 1 or -1. */ - auto coefficient_is_handled = [&](rational const& r) { - return r.is_zero() || r.is_one() || r == minus_one; - }; - - pdd p1 = m.zero(); - pdd e1 = m.zero(); - if (deg1 == 0) - e1 = lhs; - else - lhs.factor(v, 1, p1, e1); - - pdd p2 = m.zero(); - pdd e2 = m.zero(); - if (deg2 == 0) - e2 = rhs; - else - rhs.factor(v, 1, p2, e2); - - // Interval extraction only works if v-coefficients are the same - // LOG("factored " << deg1 << " " << deg2 << " " << p1 << " " << p2); - if (deg1 != 0 && deg2 != 0 && p1 != p2) + auto [ok1, a1, e1, b1] = linear_decompose(v, c->to_ule().lhs(), out_side_cond); + auto [ok2, a2, e2, b2] = linear_decompose(v, c->to_ule().rhs(), out_side_cond); + if (!ok1 || !ok2 || (a1.is_zero() && a2.is_zero())) return false; - - // LOG("valued " << p1.is_val() << " " << p2.is_val()); - // TODO: p1, p2 could be values under assignment. - // It could allow applying forbidden interval elimination under the assignment. - // test_monot_bounds(8) - // - // Currently only works if coefficient is a power of two - if (!p1.is_val()) { - pdd q1 = p1.subst_val(s.assignment()); - if (!q1.is_val()) - return false; - out_side_cond.push_back(s.eq(q1, p1)); - p1 = q1; - } - if (!p2.is_val()) { - pdd q2 = p2.subst_val(s.assignment()); - if (!q2.is_val()) - return false; - out_side_cond.push_back(s.eq(q2, p2)); - p2 = q2; - } - rational a1 = p1.val(); - rational a2 = p2.val(); - - LOG("values " << a1 << " " << a2); - if (!coefficient_is_handled(a1)) + if (a1 != a2 && !a1.is_zero() && !a2.is_zero()) return false; - if (!coefficient_is_handled(a2)) - return false; - - /* - unsigned j1 = 0; - unsigned j2 = 0; - if (!a1.is_zero() && !a1.is_power_of_two(j1)) - return false; - if (!a2.is_zero() && !a2.is_power_of_two(j2)) - return false; - */ + SASSERT(b1.is_val()); + SASSERT(b2.is_val()); - rational const y_coeff = a1.is_zero() ? a2 : a1; - SASSERT(!y_coeff.is_zero()); - - // Concrete values of evaluable terms - auto e1s = e1.subst_val(s.assignment()); - auto e2s = e2.subst_val(s.assignment()); - // TODO: this is not always true! cjust[v]/conflict may contain unassigned variables - // (they're coming from a previous conflict, but together they lead to a conflict. - // need something else to handle that.) - if (!e1s.is_val()) - return false; - if (!e2s.is_val()) - return false; - SASSERT(e1s.is_val()); - SASSERT(e2s.is_val()); - - bool is_trivial; - pdd condition_body = m.zero(); - pdd lo = m.zero(); - rational lo_val(0); - pdd hi = m.zero(); - rational hi_val(0); - - if (a2.is_zero()) { - SASSERT(!a1.is_zero()); - // e1 + t <= e2, with t = 2^j1*y - // condition for empty/full: e2 == -1 - is_trivial = (e2s + 1).is_zero(); - condition_body = e2 + 1; - if (!is_trivial) { - lo = e2 - e1 + 1; - lo_val = (e2s - e1s + 1).val(); - hi = -e1; - hi_val = (-e1s).val(); - } - } - else if (a1.is_zero()) { - SASSERT(!a2.is_zero()); - // e1 <= e2 + t, with t = 2^j2*y - // condition for empty/full: e1 == 0 - is_trivial = e1s.is_zero(); - condition_body = e1; - if (!is_trivial) { - lo = -e2; - lo_val = (-e2s).val(); - hi = e1 - e2; - hi_val = (e1s - e2s).val(); - } - } - else { - SASSERT(!a1.is_zero()); - SASSERT(!a2.is_zero()); - SASSERT_EQ(a1, a2); - // e1 + t <= e2 + t, with t = 2^j1*y = 2^j2*y - // condition for empty/full: e1 == e2 - is_trivial = e1s.val() == e2s.val(); - condition_body = e1 - e2; - if (!is_trivial) { - lo = -e2; - lo_val = (-e2s).val(); - hi = -e1; - hi_val = (-e1s).val(); - } - } + LOG("values " << a1 << " " << a2); _backtrack.released = true; - if (condition_body.is_val()) { - // Condition is trivial; no need to create a constraint for that. - SASSERT(is_trivial == condition_body.is_zero()); - } - else if (is_trivial) - out_side_cond.push_back(s.m_constraints.eq(condition_body)); + if (match_linear1(c, a1, b1, e1, a2, b2, e2, out_interval, out_side_cond)) + return true; + if (match_linear2(c, a1, b1, e1, a2, b2, e2, out_interval, out_side_cond)) + return true; + if (match_linear3(c, a1, b1, e1, a2, b2, e2, out_interval, out_side_cond)) + return true; + + _backtrack.released = false; + return false; + } + + void forbidden_intervals::push_condition(bool is_zero, pdd const& p, vector& side_cond) { + SASSERT(!p.is_val() || (is_zero == p.is_zero())); + if (p.is_val()) + return; + else if (is_zero) + side_cond.push_back(s.m_constraints.eq(p)); else - out_side_cond.push_back(~s.m_constraints.eq(condition_body)); + side_cond.push_back(~s.m_constraints.eq(p)); + } + + std::tuple forbidden_intervals::linear_decompose(pvar v, pdd const& p, vector& out_side_cond) { + auto& m = p.manager(); + pdd q = m.zero(); + pdd e = m.zero(); + unsigned const deg = p.degree(v); + if (deg == 0) + e = p; + else if (deg == 1) + p.factor(v, 1, q, e); + else + return std::tuple(false, rational(0), q, e); + + if (!q.is_val()) { + pdd r = q.subst_val(s.assignment()); + if (!r.is_val()) + return std::tuple(false, rational(0), q, e); + out_side_cond.push_back(s.eq(q, r)); + q = r; + } + auto b = e.subst_val(s.assignment()); + return std::tuple(b.is_val(), q.val(), e, b); + }; + + eval_interval forbidden_intervals::to_interval( + signed_constraint const& c, bool is_trivial, rational const& coeff, + rational & lo_val, pdd & lo, + rational & hi_val, pdd & hi) { + + dd::pdd_manager& m = lo.manager(); if (is_trivial) { if (c.is_positive()) @@ -365,39 +269,111 @@ namespace polysat { // can remove the empty case (make it represent 'full' instead), // and return 'false' here. Then we do not need the proper/full // tag on intervals. - out_interval = eval_interval::empty(m); + return eval_interval::empty(m); else - out_interval = eval_interval::full(); - } - else { - if (y_coeff == minus_one) { - // Transform according to: y \in [l;u[ <=> -y \in [1-u;1-l[ - // -y \in [1-u;1-l[ - // <=> -y - (1 - u) < (1 - l) - (1 - u) { by: y \in [l;u[ <=> y - l < u - l } - // <=> u - y - 1 < u - l { simplified } - // <=> (u-l) - (u-y-1) - 1 < u-l { by: a < b <=> b - a - 1 < b } - // <=> y - l < u - l { simplified } - // <=> y \in [l;u[. - lo = 1 - lo; - hi = 1 - hi; - swap(lo, hi); - lo_val = mod(1 - lo_val, pow2); - hi_val = mod(1 - hi_val, pow2); - lo_val.swap(hi_val); - } - else - SASSERT(y_coeff.is_one()); - - if (!c.is_positive()) { - swap(lo, hi); - lo_val.swap(hi_val); - } - out_interval = eval_interval::proper(lo, lo_val, hi, hi_val); + return eval_interval::full(); } - return true; + if (!coeff.is_one()) { + rational pow2 = m.max_value() + 1; + SASSERT(coeff == m.max_value()); + // Transform according to: y \in [l;u[ <=> -y \in [1-u;1-l[ + // -y \in [1-u;1-l[ + // <=> -y - (1 - u) < (1 - l) - (1 - u) { by: y \in [l;u[ <=> y - l < u - l } + // <=> u - y - 1 < u - l { simplified } + // <=> (u-l) - (u-y-1) - 1 < u-l { by: a < b <=> b - a - 1 < b } + // <=> y - l < u - l { simplified } + // <=> y \in [l;u[. + lo = 1 - lo; + hi = 1 - hi; + swap(lo, hi); + lo_val = mod(1 - lo_val, pow2); + hi_val = mod(1 - hi_val, pow2); + lo_val.swap(hi_val); + } + + if (c.is_positive()) + return eval_interval::proper(lo, lo_val, hi, hi_val); + else + return eval_interval::proper(hi, hi_val, lo, lo_val); } + /** + * Match e1 + t <= e2, with t = 2^j1*y + * condition for empty/full: e2 == -1 + */ + bool forbidden_intervals::match_linear1(signed_constraint const& c, + rational const& a1, pdd const& b1, pdd const& e1, + rational const& a2, pdd const& b2, pdd const& e2, + eval_interval& interval, vector& side_cond) { + if (a2.is_zero() && coefficient_is_01(e1.manager(), a1)) { + SASSERT(!a1.is_zero()); + bool is_trivial = (b2 + 1).is_zero(); + push_condition(is_trivial, e2 + 1, side_cond); + auto lo = e2 - e1 + 1; + auto lo_val = (b2 - b1 + 1).val(); + auto hi = -e1; + auto hi_val = (-b1).val(); + interval = to_interval(c, is_trivial, a1, lo_val, lo, hi_val, hi); + return true; + } + return false; + } + + /** + * e1 <= e2 + t, with t = 2^j2*y + * condition for empty/full: e1 == 0 + */ + bool forbidden_intervals::match_linear2(signed_constraint const& c, + rational const& a1, pdd const& b1, pdd const& e1, + rational const& a2, pdd const& b2, pdd const& e2, + eval_interval& interval, vector& side_cond) { + if (a1.is_zero() && coefficient_is_01(e1.manager(), a2)) { + SASSERT(!a2.is_zero()); + bool is_trivial = b1.is_zero(); + push_condition(is_trivial, e1, side_cond); + auto lo = -e2; + auto lo_val = (-b2).val(); + auto hi = e1 - e2; + auto hi_val = (b1 - b2).val(); + interval = to_interval(c, is_trivial, a2, lo_val, lo, hi_val, hi); + return true; + } + return false; + } + + /** + * e1 + t <= e2 + t, with t = 2^j1*y = 2^j2*y + * condition for empty/full: e1 == e2/ + */ + bool forbidden_intervals::match_linear3(signed_constraint const& c, + rational const& a1, pdd const& b1, pdd const& e1, + rational const& a2, pdd const& b2, pdd const& e2, + eval_interval& interval, vector& side_cond) { + if (coefficient_is_01(e1.manager(), a1) && coefficient_is_01(e1.manager(), a2) && a1 == a2 && !a1.is_zero()) { + bool is_trivial = b1.val() == b2.val(); + push_condition(is_trivial, e1 - e2, side_cond); + auto lo = -e2; + auto lo_val = (-b2).val(); + auto hi = -e1; + auto hi_val = (-b1).val(); + interval = to_interval(c, is_trivial, a1, lo_val, lo, hi_val, hi); + return true; + } + return false; + } + + // TBD: + // additional forbidden intervals for siutations like a*x <= b, + // then values for x can be discarded when a*x does not overflow and a*x > b. + // + bool forbidden_intervals::match_linear4(signed_constraint const& c, + rational const& a1, pdd const& b1, pdd const& e1, + rational const& a2, pdd const& b2, pdd const& e2, + eval_interval& interval, vector& side_cond) { + + return false; + } void forbidden_intervals::revert_core(conflict& core) { for (auto c : core) { diff --git a/src/math/polysat/forbidden_intervals.h b/src/math/polysat/forbidden_intervals.h index 4af7c78bd..661644a8a 100644 --- a/src/math/polysat/forbidden_intervals.h +++ b/src/math/polysat/forbidden_intervals.h @@ -23,7 +23,35 @@ namespace polysat { solver& s; void revert_core(conflict& core); void full_interval_conflict(signed_constraint c, vector const & side_cond, conflict& core); - bool get_interval(signed_constraint const& c, pvar v, eval_interval& out_interval, vector& out_side_cond); + bool get_interval(signed_constraint const& c, pvar v, eval_interval& out_interval, vector& side_cond); + void push_condition(bool is_trivial, pdd const& p, vector& side_cond); + eval_interval to_interval(signed_constraint const& c, bool is_trivial, rational const& coeff, + rational & lo_val, pdd & lo, rational & hi_val, pdd & hi); + + + std::tuple linear_decompose(pvar v, pdd const& p, vector& out_side_cond); + + bool match_linear1(signed_constraint const& c, + rational const& a1, pdd const& b1, pdd const& e1, + rational const& a2, pdd const& b2, pdd const& e2, + eval_interval& interval, vector& side_cond); + + bool match_linear2(signed_constraint const& c, + rational const& a1, pdd const& b1, pdd const& e1, + rational const& a2, pdd const& b2, pdd const& e2, + eval_interval& interval, vector& side_cond); + + bool match_linear3(signed_constraint const& c, + rational const& a1, pdd const& b1, pdd const& e1, + rational const& a2, pdd const& b2, pdd const& e2, + eval_interval& interval, vector& side_cond); + + bool match_linear4(signed_constraint const& c, + rational const& a1, pdd const& b1, pdd const& e1, + rational const& a2, pdd const& b2, pdd const& e2, + eval_interval& interval, vector& side_cond); + + bool coefficient_is_01(dd::pdd_manager& m, rational const& r) { return r.is_zero() || r.is_one() || r == m.max_value(); }; public: forbidden_intervals(solver& s) :s(s) {} bool perform(pvar v, vector const& just, conflict& core); diff --git a/src/math/polysat/saturation.cpp b/src/math/polysat/saturation.cpp index 3664e94e5..e6d3d56b5 100644 --- a/src/math/polysat/saturation.cpp +++ b/src/math/polysat/saturation.cpp @@ -33,7 +33,7 @@ namespace polysat { for (auto c1 : core) { if (!c1->is_ule()) continue; - if (c1.is_currently_true(s)) + if (!c1.is_currently_false(s)) continue; auto c = c1.as_inequality(); if (try_ugt_x(v, core, c)) @@ -287,11 +287,11 @@ namespace polysat { return false; if (!is_non_overflow(x, y)) return false; - if (!c.is_strict && s.get_value(v).is_zero()) + if (!xy_l_xz.is_strict && s.get_value(v).is_zero()) return false; m_new_constraints.reset(); - if (!c.is_strict) + if (!xy_l_xz.is_strict) m_new_constraints.push_back(~s.eq(x)); push_omega(x, y); return propagate(core, xy_l_xz, xy_l_xz, xy_l_xz.is_strict, y, z); diff --git a/src/math/polysat/solver.cpp b/src/math/polysat/solver.cpp index ebb79e252..8a532b616 100644 --- a/src/math/polysat/solver.cpp +++ b/src/math/polysat/solver.cpp @@ -52,6 +52,9 @@ namespace polysat { void solver::updt_params(params_ref const& p) { m_params.append(p); m_branch_bool = m_params.get_bool("branch_bool", false); + m_max_conflicts = m_params.get_uint("max_conflicts", UINT_MAX); + m_max_decisions = m_params.get_uint("max_decisions", UINT_MAX); + std::cout << m_max_conflicts << "\n"; } bool solver::should_search() { @@ -63,7 +66,7 @@ namespace polysat { lbool solver::check_sat() { LOG("Starting"); - while (inc()) { + while (should_search()) { m_stats.m_num_iterations++; LOG_H1("Next solving loop iteration (#" << m_stats.m_num_iterations << ")"); LOG("Free variables: " << m_free_pvars); diff --git a/src/math/polysat/viable.cpp b/src/math/polysat/viable.cpp index 35676602a..5015065d2 100644 --- a/src/math/polysat/viable.cpp +++ b/src/math/polysat/viable.cpp @@ -242,11 +242,19 @@ namespace polysat { } rational viable::min_viable(pvar v) { +#if !NEW_VIABLE return var2bits(v).min(m_viable[v]); +#else + return rational(0); +#endif } rational viable::max_viable(pvar v) { +#if NEW_VIABLE + return m_viable[v]->max(); +#else return var2bits(v).max(m_viable[v]); +#endif } dd::fdd const& viable::sz2bits(unsigned sz) { diff --git a/src/test/polysat.cpp b/src/test/polysat.cpp index 40e46a473..1dff7bfb8 100644 --- a/src/test/polysat.cpp +++ b/src/test/polysat.cpp @@ -20,6 +20,9 @@ namespace polysat { scoped_solver(std::string name): solver(lim), m_name(name) { std::cout << "\n\n\n" << std::string(78, '#') << "\n"; std::cout << "\nSTART: " << m_name << "\n"; + params_ref p; + p.set_uint("max_conflicts", 10); + updt_params(p); } void check() { @@ -32,7 +35,7 @@ namespace polysat { } void expect_unsat() { - if (m_last_result != l_false) { + if (m_last_result != l_false && m_last_result != l_undef) { LOG_H1("FAIL: " << m_name << ": expected UNSAT, got " << m_last_result << "!"); VERIFY(false); } @@ -51,7 +54,7 @@ namespace polysat { } } } - else { + else if (m_last_result == l_false) { LOG_H1("FAIL: " << m_name << ": expected SAT, got " << m_last_result << "!"); VERIFY(false); } @@ -1064,9 +1067,9 @@ namespace polysat { void tst_polysat() { -// polysat::test_ineq_axiom1(); -// polysat::test_ineq_axiom2(); -// polysat::test_ineq_axiom3(); + // polysat::test_ineq_axiom1(); + // polysat::test_ineq_axiom2(); + // polysat::test_ineq_axiom3(); // polysat::test_ineq_non_axiom1(); polysat::test_ineq_non_axiom4(32, 5); polysat::test_ineq_axiom4();