diff --git a/src/sat/dimacs.cpp b/src/sat/dimacs.cpp index 9c9a40a24..4a818af6c 100644 --- a/src/sat/dimacs.cpp +++ b/src/sat/dimacs.cpp @@ -112,6 +112,27 @@ static void read_clause(Buffer & in, std::ostream& err, sat::literal_vector & li } } +template +static void read_pragma(Buffer & in, std::ostream& err, std::string& p) { + skip_whitespace(in); + if (*in != 'p') + return; + ++in; + while (*in == ' ') + ++in; + while (true) { + if (*in == EOF) + return; + if (*in == '\n') { + ++in; + return; + } + p.push_back(*in); + ++in; + } +} + + template static bool parse_dimacs_core(Buffer & in, std::ostream& err, sat::solver & solver) { sat::literal_vector lits; @@ -156,7 +177,9 @@ namespace dimacs { sat::status_pp pp(r.m_status, p.th); switch (r.m_tag) { case drat_record::tag_t::is_clause: - return out << pp << " " << r.m_lits << " 0\n"; + if (!r.m_pragma.empty()) + return out << pp << " " << r.m_lits << " 0 p " << r.m_pragma << "\n"; + return out << pp << " " << r.m_lits << " 0\n"; case drat_record::tag_t::is_node: return out << "e " << r.m_node_id << " " << r.m_name << " " << r.m_args << "0\n"; case drat_record::tag_t::is_sort: @@ -280,6 +303,7 @@ namespace dimacs { try { loop: skip_whitespace(in); + m_record.m_pragma.clear(); switch (*in) { case EOF: return false; @@ -304,6 +328,7 @@ namespace dimacs { theory_id = read_theory_id(); skip_whitespace(in); read_clause(in, err, m_record.m_lits); + read_pragma(in, err, m_record.m_pragma); m_record.m_tag = drat_record::tag_t::is_clause; m_record.m_status = sat::status::th(false, theory_id); break; diff --git a/src/sat/dimacs.h b/src/sat/dimacs.h index cc4b27182..7b4e8557c 100644 --- a/src/sat/dimacs.h +++ b/src/sat/dimacs.h @@ -59,10 +59,11 @@ namespace dimacs { // a node populates m_node_id, m_name, m_args // a bool def populates m_node_id and one element in m_args sat::literal_vector m_lits; - sat::status m_status{ sat::status::redundant() }; - unsigned m_node_id{ 0 }; + sat::status m_status = sat::status::redundant(); + unsigned m_node_id = 0; std::string m_name; unsigned_vector m_args; + std::string m_pragma; }; struct drat_pp { diff --git a/src/sat/sat_drat.cpp b/src/sat/sat_drat.cpp index 6d8c4d477..72872e370 100644 --- a/src/sat/sat_drat.cpp +++ b/src/sat/sat_drat.cpp @@ -137,6 +137,14 @@ namespace sat { } } buffer[len++] = '0'; + if (st.get_pragma()) { + buffer[len++] = ' '; + buffer[len++] = 'p'; + buffer[len++] = ' '; + char const* ps = st.get_pragma(); + while (*ps) + buffer[len++] = *ps++; + } buffer[len++] = '\n'; m_out->write(buffer, len); diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index 57300a705..12c013734 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -98,22 +98,24 @@ namespace sat { enum class st { input, asserted, redundant, deleted }; st m_st; int m_orig; + char const* m_pragma; public: - status(st s, int o) : m_st(s), m_orig(o) {}; - status(status const& s) : m_st(s.m_st), m_orig(s.m_orig) {} - status(status&& s) noexcept { m_st = st::asserted; m_orig = -1; std::swap(m_st, s.m_st); std::swap(m_orig, s.m_orig); } + status(st s, int o, char const* ps = nullptr) : m_st(s), m_orig(o), m_pragma(ps) {}; + status(status const& s) : m_st(s.m_st), m_orig(s.m_orig), m_pragma(s.m_pragma) {} + status(status&& s) noexcept { m_st = st::asserted; m_orig = -1; std::swap(m_st, s.m_st); std::swap(m_orig, s.m_orig); std::swap(m_pragma, s.m_pragma); } status& operator=(status const& other) { m_st = other.m_st; m_orig = other.m_orig; return *this; } static status redundant() { return status(status::st::redundant, -1); } static status asserted() { return status(status::st::asserted, -1); } static status deleted() { return status(status::st::deleted, -1); } static status input() { return status(status::st::input, -1); } - static status th(bool redundant, int id) { return status(redundant ? st::redundant : st::asserted, id); } + static status th(bool redundant, int id, char const* ps = nullptr) { return status(redundant ? st::redundant : st::asserted, id, ps); } bool is_input() const { return st::input == m_st; } bool is_redundant() const { return st::redundant == m_st; } bool is_asserted() const { return st::asserted == m_st; } bool is_deleted() const { return st::deleted == m_st; } + char const* get_pragma() const { return m_pragma; } bool is_sat() const { return -1 == m_orig; } int get_th() const { return m_orig; } diff --git a/src/sat/smt/arith_axioms.cpp b/src/sat/smt/arith_axioms.cpp index 1a60f597f..7efd83ead 100644 --- a/src/sat/smt/arith_axioms.cpp +++ b/src/sat/smt/arith_axioms.cpp @@ -233,45 +233,46 @@ namespace arith { SASSERT(b1.get_var() == b2.get_var()); if (k1 == k2 && kind1 == kind2) return; SASSERT(k1 != k2 || kind1 != kind2); + char const* bound_params = "farkas 1 1"; if (kind1 == lp_api::lower_t) { if (kind2 == lp_api::lower_t) { if (k2 <= k1) - add_clause(~l1, l2); + add_clause(~l1, l2, bound_params); else - add_clause(l1, ~l2); + add_clause(l1, ~l2, bound_params); } else if (k1 <= k2) // k1 <= k2, k1 <= x or x <= k2 add_clause(l1, l2); else { // k1 > hi_inf, k1 <= x => ~(x <= hi_inf) - add_clause(~l1, ~l2); + add_clause(~l1, ~l2, bound_params); if (v_is_int && k1 == k2 + rational(1)) // k1 <= x or x <= k1-1 - add_clause(l1, l2); + add_clause(l1, l2, bound_params); } } else if (kind2 == lp_api::lower_t) { if (k1 >= k2) // k1 >= lo_inf, k1 >= x or lo_inf <= x - add_clause(l1, l2); + add_clause(l1, l2, bound_params); else { // k1 < k2, k2 <= x => ~(x <= k1) - add_clause(~l1, ~l2); + add_clause(~l1, ~l2, bound_params); if (v_is_int && k1 == k2 - rational(1)) // x <= k1 or k1+l <= x - add_clause(l1, l2); + add_clause(l1, l2, bound_params); } } else { // kind1 == A_UPPER, kind2 == A_UPPER if (k1 >= k2) // k1 >= k2, x <= k2 => x <= k1 - add_clause(l1, ~l2); + add_clause(l1, ~l2, bound_params); else // k1 <= hi_sup , x <= k1 => x <= hi_sup - add_clause(~l1, l2); + add_clause(~l1, l2, bound_params); } } @@ -498,8 +499,8 @@ namespace arith { if (x->get_root() == y->get_root()) return; reset_evidence(); - set_evidence(ci1, m_core, m_eqs); - set_evidence(ci2, m_core, m_eqs); + set_evidence(ci1); + set_evidence(ci2); ++m_stats.m_fixed_eqs; auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, x, y); ctx.propagate(x, y, jst->to_index()); diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index a6eaeffbf..151bc81d1 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -194,11 +194,8 @@ namespace arith { ++m_stats.m_bound_propagations2; reset_evidence(); m_core.push_back(lit1); - m_params.push_back(parameter(m_farkas)); - m_params.push_back(parameter(rational(1))); - m_params.push_back(parameter(rational(1))); TRACE("arith", tout << lit2 << " <- " << m_core << "\n";); - assign(lit2, m_core, m_eqs, m_params); + assign(lit2, m_core, m_eqs, "farkas 1 1"); ++m_stats.m_bounds_propagations; } @@ -239,13 +236,11 @@ namespace arith { bool first = true; for (unsigned i = 0; i < bounds.size(); ++i) { api_bound* b = bounds[i]; - if (s().value(b->get_lit()) != l_undef) { + if (s().value(b->get_lit()) != l_undef) continue; - } literal lit = is_bound_implied(be.kind(), be.m_bound, *b); - if (lit == sat::null_literal) { + if (lit == sat::null_literal) continue; - } TRACE("arith", tout << lit << " bound: " << *b << " first: " << first << "\n";); lp().settings().stats().m_num_of_implied_bounds++; @@ -260,7 +255,7 @@ namespace arith { TRACE("arith", for (auto lit : m_core) tout << lit << ": " << s().value(lit) << "\n";); DEBUG_CODE(for (auto lit : m_core) { VERIFY(s().value(lit) == l_true); }); ++m_stats.m_bound_propagations1; - assign(lit, m_core, m_eqs, m_params); + assign(lit, m_core, m_eqs, "bounds"); } if (should_refine_bounds() && first) @@ -297,7 +292,7 @@ namespace arith { void solver::consume(rational const& v, lp::constraint_index j) { - set_evidence(j, m_core, m_eqs); + set_evidence(j); m_explanation.add_pair(j, v); } @@ -318,7 +313,7 @@ namespace arith { return false; reset_evidence(); for (auto ev : e) - set_evidence(ev.ci(), m_core, m_eqs); + set_evidence(ev.ci()); auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, n1, n2); ctx.propagate(n1, n2, jst->to_index()); return true; @@ -375,7 +370,7 @@ namespace arith { reset_evidence(); m_explanation.clear(); lp().explain_implied_bound(be, m_bp); - assign(bound, m_core, m_eqs, m_params); + assign(bound, m_core, m_eqs, nullptr); } @@ -748,10 +743,10 @@ namespace arith { ++m_stats.m_fixed_eqs; reset_evidence(); - set_evidence(ci1, m_core, m_eqs); - set_evidence(ci2, m_core, m_eqs); - set_evidence(ci3, m_core, m_eqs); - set_evidence(ci4, m_core, m_eqs); + set_evidence(ci1); + set_evidence(ci2); + set_evidence(ci3); + set_evidence(ci4); enode* x = var2enode(v1); enode* y = var2enode(v2); auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, x, y); @@ -1161,13 +1156,13 @@ namespace arith { // m_explanation implies term <= k reset_evidence(); for (auto ev : m_explanation) - set_evidence(ev.ci(), m_core, m_eqs); + set_evidence(ev.ci()); // The call mk_bound() can set the m_infeasible_column in lar_solver // so the explanation is safer to take before this call. app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper()); IF_VERBOSE(4, verbose_stream() << "cut " << b << "\n"); literal lit = expr2literal(b); - assign(lit, m_core, m_eqs, m_params); + assign(lit, m_core, m_eqs, nullptr); lia_check = l_false; break; } @@ -1189,20 +1184,16 @@ namespace arith { return lia_check; } - void solver::assign(literal lit, literal_vector const& core, svector const& eqs, vector const& params) { - std::cout << "assign: "; - for (auto const& p : params) - std::cout << p << " "; - std::cout << "\n"; + void solver::assign(literal lit, literal_vector const& core, svector const& eqs, char const* pma) { if (core.size() < small_lemma_size() && eqs.empty()) { m_core2.reset(); for (auto const& c : core) m_core2.push_back(~c); m_core2.push_back(lit); - add_clause(m_core2); + add_clause(m_core2, pma); } else { - auto* jst = euf::th_explain::propagate(*this, core, eqs, lit); + auto* jst = euf::th_explain::propagate(*this, core, eqs, lit, pma); ctx.propagate(lit, jst->to_index()); } } @@ -1225,7 +1216,7 @@ namespace arith { ++m_num_conflicts; ++m_stats.m_conflicts; for (auto ev : m_explanation) - set_evidence(ev.ci(), m_core, m_eqs); + set_evidence(ev.ci()); TRACE("arith", tout << "Lemma - " << (is_conflict ? "conflict" : "propagation") << "\n"; for (literal c : m_core) tout << literal2expr(c) << "\n"; @@ -1247,7 +1238,7 @@ namespace arith { return lp().get_status() == lp::lp_status::INFEASIBLE; } - void solver::set_evidence(lp::constraint_index idx, literal_vector& core, svector& eqs) { + void solver::set_evidence(lp::constraint_index idx) { if (idx == UINT_MAX) { return; } @@ -1255,7 +1246,7 @@ namespace arith { case inequality_source: { literal lit = m_inequalities[idx]; SASSERT(lit != sat::null_literal); - core.push_back(lit); + m_core.push_back(lit); break; } case equality_source: diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index a1d9c8629..3496e42d3 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -413,8 +413,8 @@ namespace arith { void get_infeasibility_explanation_and_set_conflict(); void set_conflict(); void set_conflict_or_lemma(literal_vector const& core, bool is_conflict); - void set_evidence(lp::constraint_index idx, literal_vector& core, svector& eqs); - void assign(literal lit, literal_vector const& core, svector const& eqs, vector const& params); + void set_evidence(lp::constraint_index idx); + void assign(literal lit, literal_vector const& core, svector const& eqs, char const* pma); void false_case_of_check_nla(const nla::lemma& l); void dbg_finalize_model(model& mdl); diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index 9335e283f..8e5eb9dd7 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -166,7 +166,7 @@ namespace euf { lits.push_back(jst.lit_consequent()); if (jst.eq_consequent().first != nullptr) lits.push_back(add_lit(jst.eq_consequent())); - get_drat().add(lits, sat::status::th(m_is_redundant, jst.ext().get_id())); + get_drat().add(lits, sat::status::th(m_is_redundant, jst.ext().get_id(), jst.get_pragma())); } void solver::drat_eq_def(literal lit, expr* eq) { diff --git a/src/sat/smt/sat_th.cpp b/src/sat/smt/sat_th.cpp index f72ff601a..1f4c5b572 100644 --- a/src/sat/smt/sat_th.cpp +++ b/src/sat/smt/sat_th.cpp @@ -125,8 +125,8 @@ namespace euf { pop_core(n); } - sat::status th_euf_solver::mk_status() { - return sat::status::th(m_is_redundant, get_id()); + sat::status th_euf_solver::mk_status(char const* ps) { + return sat::status::th(m_is_redundant, get_id(), ps); } bool th_euf_solver::add_unit(sat::literal lit) { @@ -149,6 +149,11 @@ namespace euf { return add_clause(2, lits); } + bool th_euf_solver::add_clause(sat::literal a, sat::literal b, char const* ps) { + sat::literal lits[2] = { a, b }; + return add_clause(2, lits, ps); + } + bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c) { sat::literal lits[3] = { a, b, c }; return add_clause(3, lits); @@ -159,12 +164,12 @@ namespace euf { return add_clause(4, lits); } - bool th_euf_solver::add_clause(unsigned n, sat::literal* lits) { + bool th_euf_solver::add_clause(unsigned n, sat::literal* lits, char const* ps) { bool was_true = false; for (unsigned i = 0; i < n; ++i) was_true |= is_true(lits[i]); ctx.add_root(n, lits); - s().add_clause(n, lits, mk_status()); + s().add_clause(n, lits, mk_status(ps)); return !was_true; } @@ -221,37 +226,44 @@ namespace euf { return ctx.s().rand()(); } - size_t th_explain::get_obj_size(unsigned num_lits, unsigned num_eqs) { - return sat::constraint_base::obj_size(sizeof(th_explain) + sizeof(sat::literal) * num_lits + sizeof(enode_pair) * num_eqs); + size_t th_explain::get_obj_size(unsigned num_lits, unsigned num_eqs, char const* pma) { + return sat::constraint_base::obj_size(sizeof(th_explain) + sizeof(sat::literal) * num_lits + sizeof(enode_pair) * num_eqs + (pma?strlen(pma)+1:1)); } - th_explain::th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& p) { + th_explain::th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& p, char const* pma) { m_consequent = c; m_eq = p; m_num_literals = n_lits; m_num_eqs = n_eqs; - m_literals = reinterpret_cast(reinterpret_cast(this) + sizeof(th_explain)); - for (unsigned i = 0; i < n_lits; ++i) + char * base_ptr = reinterpret_cast(this) + sizeof(th_explain); + m_literals = reinterpret_cast(base_ptr); + unsigned i; + for (i = 0; i < n_lits; ++i) m_literals[i] = lits[i]; - m_eqs = reinterpret_cast(reinterpret_cast(this) + sizeof(th_explain) + sizeof(literal) * n_lits); - for (unsigned i = 0; i < n_eqs; ++i) - m_eqs[i] = eqs[i]; - + base_ptr += sizeof(literal) * n_lits; + m_eqs = reinterpret_cast(base_ptr); + for (i = 0; i < n_eqs; ++i) + m_eqs[i] = eqs[i]; + base_ptr += sizeof(enode_pair) * n_eqs; + m_pragma = reinterpret_cast(base_ptr); + for (i = 0; pma && pma[i]; ++i) + m_pragma[i] = pma[i]; + m_pragma[i] = 0; } - th_explain* th_explain::mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y) { + th_explain* th_explain::mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, char const* pma) { region& r = th.ctx.get_region(); - void* mem = r.allocate(get_obj_size(n_lits, n_eqs)); + void* mem = r.allocate(get_obj_size(n_lits, n_eqs, pma)); sat::constraint_base::initialize(mem, &th); return new (sat::constraint_base::ptr2mem(mem)) th_explain(n_lits, lits, n_eqs, eqs, c, enode_pair(x, y)); } - th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent) { - return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), consequent, nullptr, nullptr); + th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, char const* pma) { + return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), consequent, nullptr, nullptr, pma); } - th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y) { - return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), sat::null_literal, x, y); + th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, char const* pma) { + return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), sat::null_literal, x, y, pma); } th_explain* th_explain::propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y) { @@ -293,6 +305,8 @@ namespace euf { out << "--> " << m_consequent; if (m_eq.first != nullptr) out << "--> " << m_eq.first->get_expr_id() << " == " << m_eq.second->get_expr_id(); + if (m_pragma != nullptr) + out << " p " << m_pragma; return out; } diff --git a/src/sat/smt/sat_th.h b/src/sat/smt/sat_th.h index dbd042e98..ffc9d4247 100644 --- a/src/sat/smt/sat_th.h +++ b/src/sat/smt/sat_th.h @@ -143,15 +143,16 @@ namespace euf { region& get_region(); - sat::status mk_status(); + sat::status mk_status(char const* ps = nullptr); bool add_unit(sat::literal lit); bool add_units(sat::literal_vector const& lits); bool add_clause(sat::literal lit) { return add_unit(lit); } bool add_clause(sat::literal a, sat::literal b); + bool add_clause(sat::literal a, sat::literal b, char const* ps); bool add_clause(sat::literal a, sat::literal b, sat::literal c); bool add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d); - bool add_clause(sat::literal_vector const& lits) { return add_clause(lits.size(), lits.data()); } - bool add_clause(unsigned n, sat::literal* lits); + bool add_clause(sat::literal_vector const& lits, char const* ps = nullptr) { return add_clause(lits.size(), lits.data(), ps); } + bool add_clause(unsigned n, sat::literal* lits, char const* ps = nullptr); void add_equiv(sat::literal a, sat::literal b); void add_equiv_and(sat::literal a, sat::literal_vector const& bs); @@ -213,15 +214,16 @@ namespace euf { * that retrieve literals on demand. */ class th_explain { - sat::literal m_consequent { sat::null_literal }; // literal consequent for propagations - enode_pair m_eq { enode_pair() }; // equality consequent for propagations + sat::literal m_consequent = sat::null_literal; // literal consequent for propagations + enode_pair m_eq = enode_pair(); // equality consequent for propagations unsigned m_num_literals; unsigned m_num_eqs; sat::literal* m_literals; enode_pair* m_eqs; - static size_t get_obj_size(unsigned num_lits, unsigned num_eqs); - th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& eq); - static th_explain* mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y); + char* m_pragma; + static size_t get_obj_size(unsigned num_lits, unsigned num_eqs, char const* pma); + th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& eq, char const* pma = nullptr); + static th_explain* mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, char const* pma = nullptr); public: static th_explain* conflict(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs); @@ -232,8 +234,8 @@ namespace euf { static th_explain* conflict(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y); static th_explain* conflict(th_euf_solver& th, euf::enode* x, euf::enode* y); static th_explain* propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y); - static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent); - static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y); + static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, char const* pma = nullptr); + static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, char const* pma = nullptr); sat::ext_constraint_idx to_index() const { return sat::constraint_base::mem2base(this); @@ -268,6 +270,8 @@ namespace euf { enode_pair eq_consequent() const { return m_eq; } + char const* get_pragma() const { return *m_pragma ? m_pragma : nullptr; } + };