3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-24 01:25:31 +00:00

bv and gc of literals (#4692)

* bv and gc of literals

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* overload

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* diseq

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* diseq

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2020-09-17 14:24:07 -07:00 committed by GitHub
parent 2d52367368
commit 549753845e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 1480 additions and 854 deletions

View file

@ -5,6 +5,7 @@ z3_add_component(sat
sat_aig_finder.cpp
sat_anf_simplifier.cpp
sat_asymm_branch.cpp
sat_bcd.cpp
sat_big.cpp
sat_binspr.cpp
sat_clause.cpp
@ -18,7 +19,7 @@ z3_add_component(sat
sat_drat.cpp
sat_elim_eqs.cpp
sat_elim_vars.cpp
sat_bcd.cpp
sat_gc.cpp
sat_integrity_checker.cpp
sat_local_search.cpp
sat_lookahead.cpp

View file

@ -235,12 +235,15 @@ namespace dimacs {
skip_whitespace(in);
switch (*in) {
case EOF:
return false;
return false;
case 'c':
// parse comment line
case 'p':
// parse meta-data information
skip_line(in);
goto loop;
case 'i':
// parse input clause
++in;
skip_whitespace(in);
read_clause(in, err, m_record.m_lits);
@ -248,6 +251,7 @@ namespace dimacs {
m_record.m_status = sat::status::input();
break;
case 'a':
// parse non-redundant theory clause
++in;
skip_whitespace(in);
theory_id = read_theory_id();
@ -256,16 +260,32 @@ namespace dimacs {
m_record.m_tag = drat_record::tag_t::is_clause;
m_record.m_status = sat::status::th(false, theory_id);
break;
case 'g':
// parse garbage collected Boolean variable
++in;
skip_whitespace(in);
b = parse_int(in, err);
e = parse_int(in, err);
if (e != 0 || b <= 0)
throw lex_error();
m_record.m_tag = drat_record::tag_t::is_var_gc;
m_record.m_node_id = b;
break;
case 'e':
// parse expression definition
parse_ast(drat_record::tag_t::is_node);
break;
case 'f':
// parse function declaration
parse_ast(drat_record::tag_t::is_decl);
break;
case 's':
// parse sort declaration (not used)
parse_ast(drat_record::tag_t::is_sort);
break;
case 'b':
// parse bridge between Boolean variable identifier b
// and expression identifier e, which is of type Bool
++in;
skip_whitespace(in);
b = parse_int(in, err);
@ -279,6 +299,7 @@ namespace dimacs {
m_record.m_args.push_back(n);
break;
case 'd':
// parse clause deletion
++in;
skip_whitespace(in);
read_clause(in, err, m_record.m_lits);
@ -286,6 +307,8 @@ namespace dimacs {
m_record.m_status = sat::status::deleted();
break;
case 'r':
// parse redundant theory clause
// the clause must be DRUP redundant modulo T
++in;
skip_whitespace(in);
theory_id = read_theory_id();
@ -294,6 +317,7 @@ namespace dimacs {
m_record.m_status = sat::status::th(true, theory_id);
break;
default:
// parse clause redundant modulo DRAT (or mostly just DRUP)
read_clause(in, err, m_record.m_lits);
m_record.m_tag = drat_record::tag_t::is_clause;
m_record.m_status = sat::status::redundant();

View file

@ -53,7 +53,7 @@ namespace dimacs {
};
struct drat_record {
enum class tag_t { is_clause, is_node, is_decl, is_sort, is_bool_def };
enum class tag_t { is_clause, is_node, is_decl, is_sort, is_bool_def, is_var_gc };
tag_t m_tag{ tag_t::is_clause };
// a clause populates m_lits and m_status
// a node populates m_node_id, m_name, m_args

View file

@ -165,6 +165,7 @@ namespace sat {
explicit clause_wrapper(clause & c):m_cls(&c), m_l2_idx(null_literal.to_uint()) {}
bool is_binary() const { return m_l2_idx != null_literal.to_uint(); }
bool is_ternary() const { return size() == 3; }
unsigned size() const { return is_binary() ? 2 : m_cls->size(); }
literal operator[](unsigned idx) const {
SASSERT(idx < size());
@ -178,6 +179,20 @@ namespace sat {
bool contains(bool_var v) const;
clause * get_clause() const { SASSERT(!is_binary()); return m_cls; }
bool was_removed() const { return !is_binary() && get_clause()->was_removed(); }
bool is_learned() const { return !is_binary() && get_clause()->is_learned(); }
class iterator {
unsigned m_idx;
clause_wrapper const& m_cw;
public:
iterator(clause_wrapper const& c, unsigned idx): m_idx(idx), m_cw(c) {}
iterator& operator++() { ++m_idx; return *this; }
literal operator*() { return m_cw[m_idx]; }
bool operator==(iterator const& other) const { SASSERT(&m_cw == &other.m_cw); return m_idx == other.m_idx; }
bool operator!=(iterator const& other) const { SASSERT(&m_cw == &other.m_cw); return m_idx != other.m_idx; }
};
iterator begin() const { return iterator(*this, 0); }
iterator end() const { return iterator(*this, size()); }
};
typedef svector<clause_wrapper> clause_wrapper_vector;

View file

@ -257,6 +257,22 @@ namespace sat {
}
}
void drat::gc_var(bool_var v) {
sat::literal l(v, false);
// TBD: we want to remove all clauses that mention v.
std::cout << "GC " << v << "\n";
m_watches[l.index()].reset();
m_watches[(~l).index()].reset();
if (m_assignment[l.var()] != l_undef) {
unsigned j = 0;
for (literal lit : m_units)
if (lit.var() != v)
m_units[j++] = lit;
m_units.shrink(j);
m_assignment[l.var()] = l_undef;
}
}
void drat::bool_def(bool_var v, unsigned n) {
if (m_out)
(*m_out) << "b " << v << " " << n << " 0\n";
@ -277,6 +293,11 @@ namespace sat {
(*m_out) << " 0\n";
}
void drat::log_gc_var(bool_var v) {
if (m_out)
(*m_out) << "g " << v << " 0\n";
}
void drat::log_adhoc(std::function<void(std::ostream&)>& fn) {
if (m_out)
fn(*m_out);

View file

@ -38,6 +38,12 @@ Notes:
Redundant clause (theory lemma if theory id is given)
[r [<theory-id>]] <literal>* 0
Declaration of an auxiliary function:
f <smtlib2-function-declaration> 0
Garbage collection of a Boolean variable:
g <bool-var-id> 0
Available theories are:
- euf The theory lemma should be a consequence of congruence closure.
- ba TBD (need to also log cardinality and pb constraints)
@ -125,6 +131,8 @@ namespace sat {
void add(literal_vector const& c); // add learned clause
void add(unsigned sz, literal const* lits, status st);
void gc_var(bool_var v);
// support for SMT - connect Boolean variables with AST nodes
// associate AST node id with Boolean variable v
void bool_def(bool_var v, unsigned n);
@ -134,6 +142,8 @@ namespace sat {
void def_add_arg(unsigned arg);
void def_end();
void log_gc_var(bool_var v);
// ad-hoc logging until a format is developed
void log_adhoc(std::function<void(std::ostream&)>& fn);

562
src/sat/sat_gc.cpp Normal file
View file

@ -0,0 +1,562 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
sat_solver.cpp
Abstract:
SAT solver main class.
Author:
Leonardo de Moura (leonardo) 2011-05-21.
Revision History:
--*/
#include "sat/sat_solver.h"
#define ENABLE_TERNARY true
namespace sat {
// -----------------------
//
// GC
//
// -----------------------
bool solver::should_gc() const {
return
m_conflicts_since_gc > m_gc_threshold &&
(m_config.m_gc_strategy != GC_DYN_PSM || at_base_lvl());
}
void solver::do_gc() {
if (!should_gc()) return;
TRACE("sat", tout << m_conflicts_since_gc << " " << m_gc_threshold << "\n";);
unsigned gc = m_stats.m_gc_clause;
m_conflicts_since_gc = 0;
m_gc_threshold += m_config.m_gc_increment;
IF_VERBOSE(10, verbose_stream() << "(sat.gc)\n";);
CASSERT("sat_gc_bug", check_invariant());
switch (m_config.m_gc_strategy) {
case GC_GLUE:
gc_glue();
break;
case GC_PSM:
gc_psm();
break;
case GC_GLUE_PSM:
gc_glue_psm();
break;
case GC_PSM_GLUE:
gc_psm_glue();
break;
case GC_DYN_PSM:
if (!m_assumptions.empty()) {
gc_glue_psm();
break;
}
if (!at_base_lvl())
return;
gc_dyn_psm();
break;
default:
UNREACHABLE();
break;
}
if (m_ext) m_ext->gc();
if (gc > 0 && should_defrag()) {
defrag_clauses();
}
CASSERT("sat_gc_bug", check_invariant());
}
/**
\brief Lex on (glue, size)
*/
struct glue_lt {
bool operator()(clause const * c1, clause const * c2) const {
if (c1->glue() < c2->glue()) return true;
return c1->glue() == c2->glue() && c1->size() < c2->size();
}
};
/**
\brief Lex on (psm, size)
*/
struct psm_lt {
bool operator()(clause const * c1, clause const * c2) const {
if (c1->psm() < c2->psm()) return true;
return c1->psm() == c2->psm() && c1->size() < c2->size();
}
};
/**
\brief Lex on (glue, psm, size)
*/
struct glue_psm_lt {
bool operator()(clause const * c1, clause const * c2) const {
if (c1->glue() < c2->glue()) return true;
if (c1->glue() > c2->glue()) return false;
if (c1->psm() < c2->psm()) return true;
if (c1->psm() > c2->psm()) return false;
return c1->size() < c2->size();
}
};
/**
\brief Lex on (psm, glue, size)
*/
struct psm_glue_lt {
bool operator()(clause const * c1, clause const * c2) const {
if (c1->psm() < c2->psm()) return true;
if (c1->psm() > c2->psm()) return false;
if (c1->glue() < c2->glue()) return true;
if (c1->glue() > c2->glue()) return false;
return c1->size() < c2->size();
}
};
void solver::gc_glue() {
std::stable_sort(m_learned.begin(), m_learned.end(), glue_lt());
gc_half("glue");
}
void solver::gc_psm() {
save_psm();
std::stable_sort(m_learned.begin(), m_learned.end(), psm_lt());
gc_half("psm");
}
void solver::gc_glue_psm() {
save_psm();
std::stable_sort(m_learned.begin(), m_learned.end(), glue_psm_lt());
gc_half("glue-psm");
}
void solver::gc_psm_glue() {
save_psm();
std::stable_sort(m_learned.begin(), m_learned.end(), psm_glue_lt());
gc_half("psm-glue");
}
/**
\brief Compute the psm of all learned clauses.
*/
void solver::save_psm() {
for (clause* cp : m_learned) {
cp->set_psm(psm(*cp));
}
}
/**
\brief GC (the second) half of the clauses in the database.
*/
void solver::gc_half(char const * st_name) {
TRACE("sat", tout << "gc\n";);
unsigned sz = m_learned.size();
unsigned new_sz = sz/2; // std::min(sz/2, m_clauses.size()*2);
unsigned j = new_sz;
for (unsigned i = new_sz; i < sz; i++) {
clause & c = *(m_learned[i]);
if (can_delete(c)) {
detach_clause(c);
del_clause(c);
}
else {
m_learned[j] = &c;
j++;
}
}
new_sz = j;
m_stats.m_gc_clause += sz - new_sz;
m_learned.shrink(new_sz);
IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :strategy " << st_name << " :deleted " << (sz - new_sz) << ")\n";);
}
bool solver::can_delete3(literal l1, literal l2, literal l3) const {
if (value(l1) == l_true &&
value(l2) == l_false &&
value(l3) == l_false) {
justification const& j = m_justification[l1.var()];
if (j.is_ternary_clause()) {
watched w1(l2, l3);
watched w2(j.get_literal1(), j.get_literal2());
return w1 != w2;
}
}
return true;
}
bool solver::can_delete(clause const & c) const {
if (c.on_reinit_stack())
return false;
if (ENABLE_TERNARY && c.size() == 3) {
return
can_delete3(c[0],c[1],c[2]) &&
can_delete3(c[1],c[0],c[2]) &&
can_delete3(c[2],c[0],c[1]);
}
literal l0 = c[0];
if (value(l0) != l_true)
return true;
justification const & jst = m_justification[l0.var()];
return !jst.is_clause() || cls_allocator().get_clause(jst.get_clause_offset()) != &c;
}
/**
\brief Use gc based on dynamic psm. Clauses are initially frozen.
*/
void solver::gc_dyn_psm() {
TRACE("sat", tout << "gc\n";);
// To do gc at scope_lvl() > 0, I will need to use the reinitialization stack, or live with the fact
// that I may miss some propagations for reactivated clauses.
SASSERT(at_base_lvl());
// compute
// d_tk
unsigned h = 0;
unsigned V_tk = 0;
for (bool_var v = 0; v < num_vars(); v++) {
if (m_assigned_since_gc[v]) {
V_tk++;
m_assigned_since_gc[v] = false;
}
if (m_phase[v] != m_prev_phase[v]) {
h++;
m_prev_phase[v] = m_phase[v];
}
}
double d_tk = V_tk == 0 ? static_cast<double>(num_vars() + 1) : static_cast<double>(h)/static_cast<double>(V_tk);
if (d_tk < m_min_d_tk)
m_min_d_tk = d_tk;
TRACE("sat_frozen", tout << "m_min_d_tk: " << m_min_d_tk << "\n";);
unsigned frozen = 0;
unsigned deleted = 0;
unsigned activated = 0;
clause_vector::iterator it = m_learned.begin();
clause_vector::iterator it2 = it;
clause_vector::iterator end = m_learned.end();
for (; it != end; ++it) {
clause & c = *(*it);
if (!c.frozen()) {
// Active clause
if (c.glue() > m_config.m_gc_small_lbd) {
// I never delete clauses with small lbd
if (c.was_used()) {
c.reset_inact_rounds();
}
else {
c.inc_inact_rounds();
if (c.inact_rounds() > m_config.m_gc_k) {
detach_clause(c);
del_clause(c);
m_stats.m_gc_clause++;
deleted++;
continue;
}
}
c.unmark_used();
if (psm(c) > static_cast<unsigned>(c.size() * m_min_d_tk)) {
// move to frozen;
TRACE("sat_frozen", tout << "freezing size: " << c.size() << " psm: " << psm(c) << " " << c << "\n";);
detach_clause(c);
c.reset_inact_rounds();
c.freeze();
m_num_frozen++;
frozen++;
}
}
}
else {
// frozen clause
clause & c = *(*it);
if (psm(c) <= static_cast<unsigned>(c.size() * m_min_d_tk)) {
c.unfreeze();
m_num_frozen--;
activated++;
if (!activate_frozen_clause(c)) {
// clause was satisfied, reduced to a conflict, unit or binary clause.
del_clause(c);
continue;
}
}
else {
c.inc_inact_rounds();
if (c.inact_rounds() > m_config.m_gc_k) {
del_clause(c);
m_stats.m_gc_clause++;
deleted++;
continue;
}
}
}
*it2 = *it;
++it2;
}
m_learned.set_end(it2);
IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :d_tk " << d_tk << " :min-d_tk " << m_min_d_tk <<
" :frozen " << frozen << " :activated " << activated << " :deleted " << deleted << ")\n";);
}
// return true if should keep the clause, and false if we should delete it.
bool solver::activate_frozen_clause(clause & c) {
TRACE("sat_gc", tout << "reactivating:\n" << c << "\n";);
SASSERT(at_base_lvl());
// do some cleanup
unsigned sz = c.size();
unsigned j = 0;
for (unsigned i = 0; i < sz; i++) {
literal l = c[i];
switch (value(l)) {
case l_true:
return false;
case l_false:
break;
case l_undef:
if (i != j) {
std::swap(c[i], c[j]);
}
j++;
break;
}
}
TRACE("sat", tout << "after cleanup:\n" << mk_lits_pp(j, c.begin()) << "\n";);
unsigned new_sz = j;
switch (new_sz) {
case 0:
if (m_config.m_drat) m_drat.add();
set_conflict();
return false;
case 1:
assign_unit(c[0]);
return false;
case 2:
mk_bin_clause(c[0], c[1], true);
return false;
default:
shrink(c, sz, new_sz);
attach_clause(c);
return true;
}
}
/**
\brief Compute phase saving measure for the given clause.
*/
unsigned solver::psm(clause const & c) const {
unsigned r = 0;
for (literal l : c) {
if (l.sign() ^ m_phase[l.var()]) {
r++;
}
}
return r;
}
/**
* Control the size of the reinit-stack.
* by agressively garbage collecting lemmas that are not asserting.
*/
void solver::gc_reinit_stack(unsigned num_scopes) {
return;
SASSERT (!at_base_lvl());
unsigned new_lvl = scope_lvl() - num_scopes;
ptr_vector<clause> to_gc;
unsigned j = m_scopes[new_lvl].m_clauses_to_reinit_lim;
unsigned sz = m_clauses_to_reinit.size();
if (sz - j <= 2000)
return;
for (unsigned i = j; i < sz; ++i) {
auto cw = m_clauses_to_reinit[i];
if (cw.is_binary() || is_asserting(new_lvl, cw))
m_clauses_to_reinit[j++] = cw;
else
to_gc.push_back(cw.get_clause());
}
m_clauses_to_reinit.shrink(j);
if (to_gc.empty())
return;
for (clause* c : to_gc) {
SASSERT(c->on_reinit_stack());
c->set_removed(true);
c->set_reinit_stack(false);
}
j = 0;
for (unsigned i = 0; i < m_learned.size(); ++i) {
clause & c = *(m_learned[i]);
if (c.was_removed()) {
detach_clause(c);
del_clause(c);
}
else
m_learned[j++] = &c;
}
std::cout << "gc: " << to_gc.size() << " " << m_learned.size() << " -> " << j << "\n";
SASSERT(m_learned.size() - j == to_gc.size());
m_learned.shrink(j);
}
bool solver::is_asserting(unsigned new_lvl, clause_wrapper const& cw) const {
if (!cw.is_learned())
return true;
bool seen_true = false;
for (literal lit : cw) {
switch (value(lit)) {
case l_true:
if (lvl(lit) > new_lvl || seen_true)
return false;
seen_true = true;
continue;
case l_false:
continue;
case l_undef:
return false;
}
}
return true;
}
#if 0
void solver::gc_reinit_stack(unsigned num_scopes) {
SASSERT (!at_base_lvl());
collect_clauses_to_gc(num_scopes);
for (literal lit : m_watch_literals_to_gc) {
mark_members_of_watch_list(lit);
shrink_watch_list(lit);
}
unsigned j = 0;
for (clause* c : m_learned)
if (!c->was_removed())
m_learned[j++] = c;
m_learned.shrink(j);
for (clause* c : m_clauses_to_gc)
del_clause(*c);
m_clauses_to_gc.reset();
}
void solver::add_to_gc(literal lit, clause_wrapper const& cw) {
m_literal2gc_clause.reserve(lit.index()+1);
m_literal2gc_clause[lit.index()].push_back(cw);
if (!is_visited(lit)) {
mark_visited(lit);
m_watch_literals_to_gc.push_back(lit);
}
}
void solver::add_to_gc(clause_wrapper const& cw) {
std::cout << "add-to-gc " << cw << "\n";
if (cw.is_binary()) {
add_to_gc(cw[0], cw);
add_to_gc(cw[1], clause_wrapper(cw[1], cw[0]));
}
else if (ENABLE_TERNARY && cw.is_ternary()) {
SASSERT(cw.is_learned());
add_to_gc(cw[0], cw);
add_to_gc(cw[1], cw);
add_to_gc(cw[2], cw);
cw.get_clause()->set_removed(true);
}
else {
SASSERT(cw.is_learned());
add_to_gc(cw[0], cw);
add_to_gc(cw[1], cw);
cw.get_clause()->set_removed(true);
}
}
void solver::insert_ternary_to_delete(literal lit, clause_wrapper const& cw) {
SASSERT(cw.is_ternary());
SASSERT(lit == cw[0] || lit == cw[1] || lit == cw[2]);
literal lit1, lit2;
if (cw[0] == lit)
lit1 = cw[1], lit2 = cw[2];
else if (cw[1] == lit)
lit1 = cw[0], lit2 = cw[2];
else
lit1 = cw[0], lit2 = cw[1];
insert_ternary_to_delete(lit1, lit2, true);
}
void solver::insert_ternary_to_delete(literal lit1, literal lit2, bool should_delete) {
if (lit1.index() > lit2.index())
std::swap(lit1, lit2);
m_ternary_to_delete.push_back(std::tuple(lit1, lit2, should_delete));
}
bool solver::in_ternary_to_delete(literal lit1, literal lit2) {
if (lit1.index() > lit2.index())
std::swap(lit1, lit2);
bool found = m_ternary_to_delete.contains(std::make_pair(lit1, lit2));
if (found) std::cout << lit1 << " " << lit2 << "\n";
return found;
}
void solver::collect_clauses_to_gc(unsigned num_scopes) {
unsigned new_lvl = scope_lvl() - num_scopes;
init_visited();
m_watch_literals_to_gc.reset();
unsigned j = m_scopes[new_lvl].m_clauses_to_reinit_lim;
for (unsigned i = j, sz = m_clauses_to_reinit.size(); i < sz; ++i) {
auto cw = m_clauses_to_reinit[i];
if (is_asserting(new_lvl, cw))
m_clauses_to_reinit[j++] = cw;
else
add_to_gc(cw);
}
m_clauses_to_reinit.shrink(j);
SASSERT(m_clauses_to_reinit.size() >= j);
}
void solver::mark_members_of_watch_list(literal lit) {
init_visited();
m_ternary_to_delete.reset();
for (auto const& cw : m_literal2gc_clause[lit.index()]) {
SASSERT(!cw.is_binary() || lit == cw[0]);
SASSERT(cw.is_binary() || cw.was_removed());
if (cw.is_binary())
mark_visited(cw[1]);
else if (ENABLE_TERNARY && cw.is_ternary())
insert_ternary_to_delete(lit, cw);
}
}
void solver::shrink_watch_list(literal lit) {
auto& wl = get_wlist((~lit).index());
unsigned j = 0, sz = wl.size(), end = sz;
for (unsigned i = 0; i < end; ++i) {
auto w = wl[i];
if (w.is_binary_clause() && !is_visited(w.get_literal()))
wl[j++] = w;
else if (ENABLE_TERNARY && w.is_ternary_clause())
insert_ternary_to_delete(w.literal1(), w.literal2(), false);
else if (w.is_clause() && !get_clause(w).was_removed())
wl[j++] = w;
else if (w.is_ext_constraint())
wl[j++] = w;
}
#if 0
std::sort(m_ternary_to_delete.begin(), m_ternary_to_delete.end());
int prev = -1;
unsigned k = 0;
std::tuple<literal, literal, bool> p = tuple(null_literal, null_literal, false);
for (unsigned i = 0; i < m_ternary_to_delete.size(); ++i) {
auto const& t = m_ternary_to_delete[i];
}
#endif
std::cout << "gc-watch-list " << lit << " " << wl.size() << " -> " << j << "\n";
wl.shrink(j);
m_literal2gc_clause[lit.index()].reset();
}
#endif
}

View file

@ -202,6 +202,7 @@ namespace sat {
bool integrity_checker::check_reinit_stack() const {
for (auto const& c : s.m_clauses_to_reinit) {
SASSERT(c.is_binary() || c.get_clause()->on_reinit_stack());
VERIFY(c.is_binary() || c.get_clause()->on_reinit_stack());
}
return true;

View file

@ -167,9 +167,11 @@ namespace sat {
return;
if (m_probing_binary) {
watch_list & wlist = s.get_wlist(~l);
for (unsigned i = 0; i < wlist.size(); ++i) {
unsigned sz = s.get_wlist(~l).size();
for (unsigned i = 0; i < sz; ++i) {
watch_list& wlist = s.get_wlist(~l);
watched & w = wlist[i];
sz = wlist.size();
if (!w.is_binary_clause())
continue;
literal l2 = w.get_literal();
@ -177,7 +179,8 @@ namespace sat {
continue;
if (s.value(l2) != l_undef)
continue;
// Note: that try_lit calls propagate, which may update the watch lists.
// Note: that try_lit calls propagate, which may update the watch lists
// and potentially change the set of variables.
if (!try_lit(l2, false))
return;
if (s.inconsistent())

File diff suppressed because it is too large Load diff

View file

@ -124,6 +124,7 @@ namespace sat {
bool_vector m_lit_mark;
bool_vector m_eliminated;
bool_vector m_external;
unsigned_vector m_var_scope;
unsigned_vector m_touched;
unsigned m_touch_index;
literal_vector m_replay_assign;
@ -286,6 +287,8 @@ namespace sat {
clause * mk_ter_clause(literal * lits, status st);
bool attach_ter_clause(clause & c, status st);
clause * mk_nary_clause(unsigned num_lits, literal * lits, status st);
bool has_variables_to_reinit(clause const& c) const;
bool has_variables_to_reinit(literal l1, literal l2) const;
bool attach_nary_clause(clause & c);
void attach_clause(clause & c, bool & reinit);
void attach_clause(clause & c) { bool reinit; attach_clause(c, reinit); }
@ -320,6 +323,7 @@ namespace sat {
void detach_nary_clause(clause & c);
void detach_ter_clause(clause & c);
void push_reinit_stack(clause & c);
void push_reinit_stack(literal l1, literal l2);
void init_visited();
void mark_visited(literal l) { m_visited[l.index()] = m_visited_ts; }
@ -441,6 +445,7 @@ namespace sat {
protected:
bool should_propagate() const;
bool propagate_core(bool update);
bool propagate_literal(literal l, bool update);
// -----------------------
//
@ -546,6 +551,10 @@ namespace sat {
bool can_delete(clause const & c) const;
bool can_delete3(literal l1, literal l2, literal l3) const;
// gc for lemmas in the reinit-stack
void gc_reinit_stack(unsigned num_scopes);
bool is_asserting(unsigned new_lvl, clause_wrapper const& cw) const;
clause& get_clause(watch_list::iterator it) const {
SASSERT(it->get_kind() == watched::CLAUSE);
return get_clause(it->get_clause_offset());

View file

@ -84,6 +84,7 @@ namespace array {
}
sat::check_result solver::check() {
force_push();
// flet<bool> _is_redundant(m_is_redundant, true);
bool turn[2] = { false, false };
turn[s().rand()(2)] = true;
@ -96,14 +97,8 @@ namespace array {
return sat::check_result::CR_DONE;
}
void solver::push() {
th_euf_solver::lazy_push();
}
void solver::pop(unsigned n) {
n = lazy_pop(n);
if (n == 0)
return;
void solver::pop_core(unsigned n) {
th_euf_solver::pop_core(n);
m_var_data.resize(get_num_vars());
}
@ -111,9 +106,9 @@ namespace array {
for (unsigned i = 0; i < get_num_vars(); ++i) {
auto& d = get_var_data(i);
out << var2enode(i)->get_expr_id() << " " << mk_bounded_pp(var2expr(i), m, 2) << "\n";
display_info(out, "parent beta", d.m_parent_lambdas);
display_info(out, "parent lambdas", d.m_parent_lambdas);
display_info(out, "parent select", d.m_parent_selects);
display_info(out, "beta ", d.m_lambdas);
display_info(out, "b ", d.m_lambdas);
}
return out;
}
@ -159,12 +154,14 @@ namespace array {
}
void solver::new_eq_eh(euf::th_eq const& eq) {
m_find.merge(eq.m_v1, eq.m_v2);
force_push();
m_find.merge(eq.v1(), eq.v2());
}
bool solver::unit_propagate() {
if (m_qhead == m_axiom_trail.size())
return false;
force_push();
bool prop = false;
ctx.push(value_trail<euf::solver, unsigned>(m_qhead));
for (; m_qhead < m_axiom_trail.size() && !s().inconsistent(); ++m_qhead)

View file

@ -175,6 +175,8 @@ namespace array {
var_data& get_var_data(euf::enode* n) { return get_var_data(n->get_th_var(get_id())); }
var_data& get_var_data(theory_var v) { return *m_var_data[v]; }
var_data const& get_var_data(theory_var v) const { return *m_var_data[v]; }
void pop_core(unsigned n) override;
// models
bool have_different_model_values(theory_var v1, theory_var v2);
@ -191,8 +193,7 @@ namespace array {
void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) override {}
void asserted(literal l) override {}
sat::check_result check() override;
void push() override;
void pop(unsigned n) override;
std::ostream& display(std::ostream& out) const override;
std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override;
std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override;

View file

@ -55,6 +55,25 @@ namespace bv {
}
}
void ackerman::used_diseq_eh(euf::theory_var v1, euf::theory_var v2) {
if (v1 == v2)
return;
if (v1 > v2)
std::swap(v1, v2);
vv* n = m_tmp_vv;
n->v1 = v1;
n->v2 = v2;
vv* other = m_table.insert_if_not_there(n);
other->m_count++;
if (other->m_count > m_propagate_high_watermark || other->m_glue == 0)
s.s().set_should_simplify();
vv::push_to_front(m_queue, other);
if (other == n) {
new_tmp();
gc();
}
}
void ackerman::update_glue(vv& v) {
unsigned sz = s.m_bits[v.v1].size();
m_diff_levels.reserve(s.s().scope_lvl() + 1, false);

View file

@ -72,6 +72,8 @@ namespace bv {
void used_eq_eh(euf::theory_var v1, euf::theory_var v2);
void used_diseq_eh(euf::theory_var v1, euf::theory_var v2);
void propagate();
};

View file

@ -62,15 +62,17 @@ namespace bv {
m_zero_one_bits.push_back(zero_one_bits());
ctx.attach_th_var(n, this, r);
TRACE("bv", tout << "mk-var: " << r << " " << n->get_expr_id() << " " << mk_pp(n->get_expr(), m) << "\n";);
TRACE("bv", tout << "mk-var: " << r << " " << n->get_expr_id() << " " << mk_bounded_pp(n->get_expr(), m) << "\n";);
return r;
}
void solver::apply_sort_cnstr(euf::enode * n, sort * s) {
force_push();
get_var(n);
}
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
force_push();
SASSERT(m.is_bool(e));
if (!visit_rec(m, e, sign, root, redundant))
return sat::null_literal;
@ -81,6 +83,7 @@ namespace bv {
}
void solver::internalize(expr* e, bool redundant) {
force_push();
visit_rec(m, e, false, false, redundant);
}
@ -337,7 +340,7 @@ namespace bv {
for (expr* b : k_bits)
args.push_back(m.mk_ite(b, m_autil.mk_int(power2(i++)), zero));
expr_ref sum(m_autil.mk_add(sz, args.c_ptr()), m);
expr_ref eq(m.mk_eq(n, sum), m);
expr_ref eq = mk_eq(n, sum);
sat::literal lit = ctx.internalize(eq, false, false, m_is_redundant);
add_unit(lit);
}
@ -367,7 +370,7 @@ namespace bv {
unsigned sz = bv.get_bv_size(n);
numeral mod = power(numeral(2), sz);
rhs = m_autil.mk_mod(e, m_autil.mk_int(mod));
expr_ref eq(m.mk_eq(lhs, rhs), m);
expr_ref eq = mk_eq(lhs, rhs);
TRACE("bv", tout << eq << "\n";);
add_unit(ctx.internalize(eq, false, false, m_is_redundant));
@ -378,9 +381,9 @@ namespace bv {
numeral div = power2(i);
rhs = m_autil.mk_idiv(e, m_autil.mk_int(div));
rhs = m_autil.mk_mod(rhs, m_autil.mk_int(2));
rhs = m.mk_eq(rhs, m_autil.mk_int(1));
rhs = mk_eq(rhs, m_autil.mk_int(1));
lhs = n_bits.get(i);
expr_ref eq(m.mk_eq(lhs, rhs), m);
expr_ref eq = mk_eq(lhs, rhs);
TRACE("bv", tout << eq << "\n";);
add_unit(ctx.internalize(eq, false, false, m_is_redundant));
}
@ -403,10 +406,10 @@ namespace bv {
void solver::internalize_carry(app* n) {
SASSERT(n->get_num_args() == 3);
literal r = ctx.get_literal(n);
literal l1 = ctx.get_literal(n->get_arg(0));
literal l2 = ctx.get_literal(n->get_arg(1));
literal l3 = ctx.get_literal(n->get_arg(2));
literal r = expr2literal(n);
literal l1 = expr2literal(n->get_arg(0));
literal l2 = expr2literal(n->get_arg(1));
literal l3 = expr2literal(n->get_arg(2));
add_clause(~r, l1, l2);
add_clause(~r, l1, l3);
add_clause(~r, l2, l3);
@ -417,7 +420,7 @@ namespace bv {
void solver::internalize_xor3(app* n) {
SASSERT(n->get_num_args() == 3);
literal r = ctx.get_literal(n);
literal r = expr2literal(n);
literal l1 = expr2literal(n->get_arg(0));
literal l2 = expr2literal(n->get_arg(1));
literal l3 = expr2literal(n->get_arg(2));
@ -483,7 +486,7 @@ namespace bv {
expr_ref out(m);
fn(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), out);
sat::literal def = ctx.internalize(out, false, false, m_is_redundant);
add_def(def, ctx.get_literal(n));
add_def(def, expr2literal(n));
}
void solver::add_def(sat::literal def, sat::literal l) {
@ -568,14 +571,18 @@ namespace bv {
}
void solver::assert_ackerman(theory_var v1, theory_var v2) {
flet<bool> _red(m_is_redundant, true);
if (v1 == v2)
return;
if (v1 > v2)
std::swap(v1, v2);
flet<bool> _red(m_is_redundant, true);
++m_stats.m_ackerman;
expr* o1 = var2expr(v1);
expr* o2 = var2expr(v2);
expr_ref oe(m.mk_eq(o1, o2), m);
expr_ref oe = mk_var_eq(v1, v2);
literal oeq = ctx.internalize(oe, false, false, m_is_redundant);
unsigned sz = get_bv_size(v1);
TRACE("bv", tout << oe << "\n";);
unsigned sz = m_bits[v1].size();
TRACE("bv", tout << "ackerman-eq: " << s().scope_lvl() << " " << oe << "\n";);
literal_vector eqs;
for (unsigned i = 0; i < sz; ++i) {
literal l1 = m_bits[v1][i];
@ -583,7 +590,7 @@ namespace bv {
expr_ref e1(m), e2(m);
e1 = bv.mk_bit2bool(o1, i);
e2 = bv.mk_bit2bool(o2, i);
expr_ref e(m.mk_eq(e1, e2), m);
expr_ref e = mk_eq(e1, e2);
literal eq = ctx.internalize(e, false, false, m_is_redundant);
add_clause(l1, ~l2, ~eq);
add_clause(~l1, l2, ~eq);

View file

@ -37,15 +37,14 @@ namespace bv {
};
class solver::bit_occs_trail : public trail<euf::solver> {
solver& s;
bit_atom& a;
var_pos_occ* m_occs;
public:
bit_occs_trail(solver& s, bit_atom& a):s(s), a(a), m_occs(a.m_occs) {}
bit_occs_trail(solver& s, bit_atom& a): a(a), m_occs(a.m_occs) {}
virtual void undo(euf::solver& euf) {
std::cout << "add back occurrences " << & a << "\n";
IF_VERBOSE(1, verbose_stream() << "add back occurrences " << & a << "\n");
a.m_occs = m_occs;
}
};
@ -57,6 +56,7 @@ namespace bv {
m_ackerman(*this),
m_bb(m, get_config()),
m_find(*this) {
ctx.get_egraph().set_th_propagates_diseqs(id);
}
void solver::fixed_var_eh(theory_var v1) {
@ -194,9 +194,37 @@ namespace bv {
}
void solver::new_eq_eh(euf::th_eq const& eq) {
TRACE("bv", tout << "new eq " << eq.m_v1 << " == " << eq.m_v2 << "\n";);
if (is_bv(eq.m_v1))
m_find.merge(eq.m_v1, eq.m_v2);
force_push();
TRACE("bv", tout << "new eq " << eq.v1() << " == " << eq.v2() << "\n";);
if (is_bv(eq.v1()))
m_find.merge(eq.v1(), eq.v2());
}
void solver::new_diseq_eh(euf::th_eq const& ne) {
theory_var v1 = ne.v1(), v2 = ne.v2();
if (!is_bv(v1))
return;
if (!get_config().m_bv_eq_axioms)
return;
TRACE("bv", tout << "diff: " << v1 << " != " << v2 << "\n";);
unsigned sz = m_bits[v1].size();
for (unsigned i = 0; i < sz; ++i) {
sat::literal a = m_bits[v1][i];
sat::literal b = m_bits[v2][i];
if (a == ~b)
return;
auto va = s().value(a);
auto vb = s().value(b);
if (va != l_undef && vb != l_undef && va != vb)
return;
}
if (s().at_search_lvl()) {
force_push();
assert_ackerman(v1, v2);
}
else
m_ackerman.used_diseq_eh(v1, v2);
}
double solver::get_reward(literal l, sat::ext_constraint_idx idx, sat::literal_occs_fun& occs) const { return 0; }
@ -209,6 +237,7 @@ namespace bv {
TRACE("bv", display_constraint(tout, idx););
switch (c.m_kind) {
case bv_justification::kind_t::bv2bit:
SASSERT(s().value(c.m_antecedent) == l_true);
r.push_back(c.m_antecedent);
ctx.add_antecedent(var2enode(c.m_v1), var2enode(c.m_v2));
break;
@ -235,9 +264,16 @@ namespace bv {
}
void solver::log_drat(bv_justification const& c) {
// this has a side-effect so changes provability:
expr_ref eq(m.mk_eq(var2expr(c.m_v1), var2expr(c.m_v2)), m);
sat::literal leq = ctx.internalize(eq, false, false, false);
// introduce dummy literal for equality.
sat::literal leq(s().num_vars() + 1, false);
expr* e1 = var2expr(c.m_v1);
expr* e2 = var2expr(c.m_v2);
expr_ref eq(m.mk_eq(e1, e2), m);
ctx.get_drat().def_begin('e', eq->get_id(), std::string("="));
ctx.get_drat().def_add_arg(e1->get_id());
ctx.get_drat().def_add_arg(e2->get_id());
ctx.get_drat().def_end();
ctx.get_drat().bool_def(leq.var(), eq->get_id());
sat::literal_vector lits;
auto add_bit = [&](sat::literal lit) {
if (s().value(lit) == l_true)
@ -263,19 +299,24 @@ namespace bv {
break;
}
ctx.get_drat().add(lits, status());
ctx.get_drat().log_gc_var(leq.var());
}
void solver::asserted(literal l) {
atom* a = get_bv2a(l.var());
TRACE("bv", tout << "asserted: " << l << "\n";);
if (a && a->is_bit())
if (a && a->is_bit()) {
force_push();
for (auto vp : a->to_bit())
m_prop_queue.push_back(vp);
}
}
bool solver::unit_propagate() {
if (m_prop_queue_head == m_prop_queue.size())
return false;
force_push();
ctx.push(value_trail<euf::solver, unsigned>(m_prop_queue_head));
for (; m_prop_queue_head < m_prop_queue.size() && !s().inconsistent(); ++m_prop_queue_head)
propagate_bits(m_prop_queue[m_prop_queue_head]);
@ -311,26 +352,26 @@ namespace bv {
}
sat::check_result solver::check() {
force_push();
SASSERT(m_prop_queue.size() == m_prop_queue_head);
return sat::check_result::CR_DONE;
}
void solver::push() {
th_euf_solver::lazy_push();
void solver::push_core() {
th_euf_solver::push_core();
m_prop_queue_lim.push_back(m_prop_queue.size());
}
void solver::pop(unsigned n) {
void solver::pop_core(unsigned n) {
SASSERT(m_num_scopes == 0);
unsigned old_sz = m_prop_queue_lim.size() - n;
m_prop_queue.shrink(m_prop_queue_lim[old_sz]);
m_prop_queue_lim.shrink(old_sz);
n = lazy_pop(n);
if (n > 0) {
old_sz = get_num_vars();
m_bits.shrink(old_sz);
m_wpos.shrink(old_sz);
m_zero_one_bits.shrink(old_sz);
}
th_euf_solver::pop_core(n);
old_sz = get_num_vars();
m_bits.shrink(old_sz);
m_wpos.shrink(old_sz);
m_zero_one_bits.shrink(old_sz);
}
void solver::pre_simplify() {}
@ -559,8 +600,7 @@ namespace bv {
SASSERT(s().inconsistent());
}
else {
if (get_config().m_bv_eq_axioms && false) {
// TODO - enable when pop_reinit is available
if (false && get_config().m_bv_eq_axioms) {
expr_ref eq(m.mk_eq(var2expr(v1), var2expr(v2)), m);
flet<bool> _is_redundant(m_is_redundant, true);
literal eq_lit = ctx.internalize(eq, false, false, m_is_redundant);

View file

@ -246,8 +246,8 @@ namespace bv {
void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector & r, bool probing) override;
void asserted(literal l) override;
sat::check_result check() override;
void push() override;
void pop(unsigned n) override;
void push_core() override;
void pop_core(unsigned n) override;
void pre_simplify() override;
void simplify() override;
bool set_root(literal l, literal r) override;
@ -270,6 +270,8 @@ namespace bv {
unsigned max_var(unsigned w) const override;
void new_eq_eh(euf::th_eq const& eq) override;
void new_diseq_eh(euf::th_eq const& ne) override;
bool use_diseqs() const override { return true; }
bool unit_propagate() override;
void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override;

View file

@ -54,9 +54,11 @@ namespace euf {
if (is_app(e) && to_app(e)->get_num_args() > 0) {
m_stack.push_back(sat::eframe(e));
return false;
}
n = m_egraph.mk(e, 0, nullptr);
attach_node(n);
}
if (auto* s = expr2solver(e))
s->internalize(e, m_is_redundant);
else
attach_node(m_egraph.mk(e, 0, nullptr));
return true;
}
@ -67,12 +69,10 @@ namespace euf {
m_args.push_back(m_egraph.find(to_app(e)->get_arg(i)));
if (root && internalize_root(to_app(e), sign, m_args))
return false;
if (auto* s = expr2solver(e)) {
s->internalize(e, m_is_redundant);
return true;
}
enode* n = m_egraph.mk(e, num, m_args.c_ptr());
attach_node(n);
if (auto* s = expr2solver(e))
s->internalize(e, m_is_redundant);
else
attach_node(m_egraph.mk(e, num, m_args.c_ptr()));
return true;
}
@ -146,7 +146,7 @@ namespace euf {
sat::literal_vector lits;
for (unsigned i = 0; i < sz; ++i) {
for (unsigned j = i + 1; j < sz; ++j) {
expr_ref eq(m.mk_eq(args[i]->get_expr(), args[j]->get_expr()), m);
expr_ref eq = mk_eq(args[i]->get_expr(), args[j]->get_expr());
sat::literal lit = internalize(eq, false, false, m_is_redundant);
lits.push_back(lit);
}
@ -167,10 +167,10 @@ namespace euf {
for (expr* arg : *e) {
expr_ref fapp(m.mk_app(f, arg), m);
expr_ref gapp(m.mk_app(g, fapp.get()), m);
expr_ref eq(m.mk_eq(gapp, arg), m);
expr_ref eq = mk_eq(gapp, arg);
sat::literal lit = internalize(eq, false, false, m_is_redundant);
s().add_clause(1, &lit, st);
eqs.push_back(m.mk_eq(fapp, a));
eqs.push_back(mk_eq(fapp, a));
}
pb_util pb(m);
expr_ref at_least2(pb.mk_at_least_k(eqs.size(), eqs.c_ptr(), 2), m);
@ -191,7 +191,7 @@ namespace euf {
if (sz <= distinct_max_args) {
for (unsigned i = 0; i < sz; ++i) {
for (unsigned j = i + 1; j < sz; ++j) {
expr_ref eq(m.mk_eq(args[i]->get_expr(), args[j]->get_expr()), m);
expr_ref eq = mk_eq(args[i]->get_expr(), args[j]->get_expr());
sat::literal lit = internalize(eq, true, false, m_is_redundant);
s().add_clause(1, &lit, st);
}
@ -208,7 +208,7 @@ namespace euf {
expr_ref fresh(m.mk_fresh_const("dist-value", u), m);
enode* n = m_egraph.mk(fresh, 0, nullptr);
n->mark_interpreted();
expr_ref eq(m.mk_eq(fapp, fresh), m);
expr_ref eq = mk_eq(fapp, fresh);
sat::literal lit = internalize(eq, false, false, m_is_redundant);
s().add_clause(1, &lit, st);
}
@ -221,23 +221,30 @@ namespace euf {
expr* c = nullptr, * th = nullptr, * el = nullptr;
if (!m.is_bool(e) && m.is_ite(e, c, th, el)) {
app* a = to_app(e);
sat::bool_var v = si.to_bool_var(c);
SASSERT(v != sat::null_bool_var);
expr_ref eq_th(m.mk_eq(a, th), m);
expr_ref eq_el(m.mk_eq(a, el), m);
expr_ref eq_th = mk_eq(a, th);
sat::literal lit_th = internalize(eq_th, false, false, m_is_redundant);
sat::literal lit_el = internalize(eq_el, false, false, m_is_redundant);
literal lits1[2] = { literal(v, true), lit_th };
literal lits2[2] = { literal(v, false), lit_el };
s().add_clause(2, lits1, st);
s().add_clause(2, lits2, st);
if (th == el) {
s().add_clause(1, &lit_th, st);
}
else {
sat::bool_var v = si.to_bool_var(c);
SASSERT(v != sat::null_bool_var);
expr_ref eq_el = mk_eq(a, el);
sat::literal lit_el = internalize(eq_el, false, false, m_is_redundant);
literal lits1[2] = { literal(v, true), lit_th };
literal lits2[2] = { literal(v, false), lit_el };
s().add_clause(2, lits1, st);
s().add_clause(2, lits2, st);
}
}
else if (m.is_distinct(e)) {
expr_ref_vector eqs(m);
unsigned sz = n->num_args();
for (unsigned i = 0; i < sz; ++i) {
for (unsigned j = i + 1; j < sz; ++j) {
expr_ref eq(m.mk_eq(n->get_arg(i)->get_expr(), n->get_arg(j)->get_expr()), m);
expr_ref eq = mk_eq(n->get_arg(i)->get_expr(), n->get_arg(j)->get_expr());
eqs.push_back(eq);
}
}
@ -308,4 +315,13 @@ namespace euf {
// TODO
// return get_theory(th_id)->is_shared(l->get_var());
}
expr_ref solver::mk_eq(expr* e1, expr* e2) {
if (e1 == e2)
return expr_ref(m.mk_true(), m);
expr_ref r(m.mk_eq(e2, e1), m);
if (!m_egraph.find(r))
r = m.mk_eq(e1, e2);
return r;
}
}

View file

@ -27,19 +27,19 @@ namespace euf {
void solver::check_eqc_bool_assignment() const {
for (enode* n : m_egraph.nodes()) {
VERIFY(!m.is_bool(n->get_expr()) ||
s().value(get_literal(n)) == s().value(get_literal(n->get_root())));
s().value(enode2literal(n)) == s().value(enode2literal(n->get_root())));
}
}
void solver::check_missing_bool_enode_propagation() const {
for (enode* n : m_egraph.nodes())
if (m.is_bool(n->get_expr()) && l_undef == s().value(get_literal(n))) {
if (m.is_bool(n->get_expr()) && l_undef == s().value(enode2literal(n))) {
if (!n->is_root()) {
VERIFY(l_undef == s().value(get_literal(n->get_root())));
VERIFY(l_undef == s().value(enode2literal(n->get_root())));
}
else
for (enode* o : enode_class(n)) {
VERIFY(l_undef == s().value(get_literal(o)));
VERIFY(l_undef == s().value(enode2literal(o)));
}
}
}

View file

@ -42,8 +42,15 @@ namespace euf {
updt_params(p);
std::function<void(std::ostream&, void*)> disp =
[&](std::ostream& out, void* j) { display_justification_ptr(out, reinterpret_cast<size_t*>(j)); };
[&](std::ostream& out, void* j) {
display_justification_ptr(out, reinterpret_cast<size_t*>(j));
};
std::function<lbool(enode* n)> eval = [&](enode* n) {
sat::literal lit = expr2literal(n->get_expr());
return (lit == sat::null_literal) ? l_undef : s().value(lit);
};
m_egraph.set_display_justification(disp);
m_egraph.set_eval(eval);
}
void solver::updt_params(params_ref const& p) {
@ -140,8 +147,8 @@ namespace euf {
ext->get_antecedents(l, idx, r, probing);
for (unsigned qhead = 0; qhead < m_explain.size(); ++qhead) {
size_t* e = m_explain[qhead];
if (is_literal(e))
r.push_back(get_literal(e));
if (is_literal(e))
r.push_back(get_literal(e));
else {
size_t idx = get_justification(e);
auto* ext = sat::constraint_base::to_extension(idx);
@ -150,9 +157,14 @@ namespace euf {
ext->get_antecedents(lit, idx, r, probing);
}
}
m_egraph.end_explain();
m_egraph.end_explain();
unsigned j = 0;
for (sat::literal lit : r)
if (s().lvl(lit) > 0) r[j++] = lit;
r.shrink(j);
TRACE("euf", tout << "eplain " << l << " <- " << r << " " << probing << "\n";);
DEBUG_CODE(for (auto lit : r) SASSERT(s().value(lit) == l_true););
if (!probing)
log_antecedents(l, r);
}
@ -204,13 +216,13 @@ namespace euf {
void solver::asserted(literal l) {
expr* e = m_var2expr.get(l.var(), nullptr);
if (!e) {
return;
}
if (!e)
return;
bool sign = l.sign();
TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << "\n";);
euf::enode* n = m_egraph.find(e);
TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << "\n";);
if (!n)
return;
for (auto th : enode_th_vars(n))
@ -220,32 +232,17 @@ namespace euf {
size_t* c = to_ptr(l);
SASSERT(is_literal(c));
SASSERT(l == get_literal(c));
if (m.is_eq(e) && n->num_args() == 2) {
if (m.is_eq(e) && n->num_args() == 2 && !sign) {
euf::enode* na = n->get_arg(0);
euf::enode* nb = n->get_arg(1);
if (!sign) {
m_egraph.merge(na, nb, c);
return;
}
else
new_diseq(na, nb, l);
m_egraph.merge(na, nb, c);
}
else {
euf::enode* nb = sign ? mk_false() : mk_true();
m_egraph.merge(n, nb, c);
}
euf::enode* nb = sign ? mk_false() : mk_true();
m_egraph.merge(n, nb, c);
}
void solver::new_diseq(enode* n1, enode* n2, literal lit) {
enode * r1 = n1->get_root();
enode * r2 = n2->get_root();
if (r1 == r2)
return;
if (r1->has_one_th_var() && r2->has_one_th_var() && r1->get_first_th_id() == r2->get_first_th_id()) {
theory_id id = r1->get_first_th_id();
theory_var v1 = r1->get_th_var(id);
theory_var v2 = r2->get_th_var(id);
fid2solver(id)->new_diseq_eh(r1, r2);
}
}
bool solver::unit_propagate() {
bool propagated = false;
@ -284,7 +281,7 @@ namespace euf {
bool_var v = si.to_bool_var(e);
SASSERT(m.is_bool(e));
size_t cnstr;
literal lit;
literal lit;
if (is_eq) {
VERIFY(m.is_eq(e, a, b));
cnstr = eq_constraint().to_index();
@ -315,7 +312,10 @@ namespace euf {
void solver::propagate_th_eqs() {
for (; m_egraph.has_th_eq() && !s().inconsistent() && !m_egraph.inconsistent(); m_egraph.next_th_eq()) {
th_eq eq = m_egraph.get_th_eq();
m_id2solver[eq.m_id]->new_eq_eh(eq);
if (eq.is_eq())
m_id2solver[eq.id()]->new_eq_eh(eq);
else
m_id2solver[eq.id()]->new_diseq_eh(eq);
}
}
@ -379,6 +379,7 @@ namespace euf {
m_scopes.shrink(m_scopes.size() - n);
si.pop(n);
SASSERT(m_egraph.num_scopes() == m_scopes.size());
TRACE("euf", tout << "pop to: " << m_scopes.size() << "\n";);
}
void solver::start_reinit(unsigned n) {
@ -405,9 +406,19 @@ namespace euf {
if (expr2var_replay.empty())
return;
si.set_expr2var_replay(&expr2var_replay);
for (auto const& kv : expr2var_replay)
attach_lit(si.internalize(kv.m_key, true), kv.m_key);
si.set_expr2var_replay(nullptr);
TRACE("euf", for (auto const& kv : expr2var_replay) tout << "replay: " << kv.m_value << " " << mk_bounded_pp(kv.m_key, m) << "\n";);
for (auto const& kv : expr2var_replay) {
sat::literal lit;
expr* e = kv.m_key;
if (si.is_bool_op(e))
lit = literal(expr2var_replay[e], false);
else
lit = si.internalize(kv.m_key, true);
VERIFY(lit.var() == kv.m_value);
attach_lit(lit, kv.m_key);
}
si.set_expr2var_replay(nullptr);
TRACE("euf", tout << "replay done\n";);
}
void solver::pre_simplify() {

View file

@ -49,8 +49,6 @@ namespace euf {
size_t to_index() const { return sat::constraint_base::mem2base(this); }
};
class solver : public sat::extension, public th_internalizer, public th_decompile {
typedef top_sort<euf::enode> deps_t;
friend class ackerman;
@ -194,10 +192,11 @@ namespace euf {
sat::sat_internalizer& get_si() { return si; }
ast_manager& get_manager() { return m; }
enode* get_enode(expr* e) { return m_egraph.find(e); }
sat::literal get_literal(expr* e) const { return literal(si.to_bool_var(e), false); }
sat::literal get_literal(enode* e) const { return get_literal(e->get_expr()); }
sat::literal expr2literal(expr* e) const { return literal(si.to_bool_var(e), false); }
sat::literal enode2literal(enode* e) const { return expr2literal(e->get_expr()); }
smt_params const& get_config() { return m_config; }
region& get_region() { return m_trail.get_region(); }
egraph& get_egraph() { return m_egraph; }
template <typename C>
void push(C const& c) { m_trail.push(c); }
euf_trail_stack& get_trail_stack() { return m_trail; }
@ -254,6 +253,8 @@ namespace euf {
void internalize(expr* e, bool learned) override;
void attach_th_var(enode* n, th_solver* th, theory_var v) { m_egraph.add_th_var(n, v, th->get_id()); }
void attach_node(euf::enode* n);
expr_ref mk_eq(expr* e1, expr* e2);
expr_ref mk_eq(euf::enode* n1, euf::enode* n2) { return mk_eq(n1->get_expr(), n2->get_expr()); }
euf::enode* mk_enode(expr* e, unsigned n, enode* const* args) { return m_egraph.mk(e, n, args); }
expr* bool_var2expr(sat::bool_var v) { return m_var2expr.get(v, nullptr); }
void unhandled_function(func_decl* f);

View file

@ -74,7 +74,7 @@ namespace euf {
}
sat::literal th_euf_solver::expr2literal(expr* e) const {
return ctx.get_literal(e);
return ctx.expr2literal(e);
}
expr* th_euf_solver::bool_var2expr(sat::bool_var v) const {
@ -98,26 +98,23 @@ namespace euf {
return get_th_var(ctx.get_enode(e));
}
void th_euf_solver::push() {
void th_euf_solver::push_core() {
TRACE("euf", tout << "push-core\n";);
m_var2enode_lim.push_back(m_var2enode.size());
}
void th_euf_solver::pop(unsigned num_scopes) {
void th_euf_solver::pop_core(unsigned num_scopes) {
unsigned new_lvl = m_var2enode_lim.size() - num_scopes;
m_var2enode.shrink(m_var2enode_lim[new_lvl]);
m_var2enode_lim.shrink(new_lvl);
}
unsigned th_euf_solver::lazy_pop(unsigned n) {
if (n <= m_num_scopes) {
m_num_scopes -= n;
return 0;
}
else {
n -= m_num_scopes;
pop(n);
return n;
}
void th_euf_solver::pop(unsigned n) {
unsigned k = std::min(m_num_scopes, n);
m_num_scopes -= k;
n -= k;
if (n > 0)
pop_core(n);
}
bool th_euf_solver::add_unit(sat::literal lit) {
@ -154,4 +151,9 @@ namespace euf {
void th_euf_solver::rewrite(expr_ref& a) {
ctx.get_rewriter()(a);
}
expr_ref th_euf_solver::mk_eq(expr* e1, expr* e2) {
return ctx.mk_eq(e1, e2);
}
}

View file

@ -95,13 +95,15 @@ namespace euf {
virtual bool use_diseqs() const { return false; }
virtual void new_diseq_eh(euf::enode* a, euf::enode* b) {}
virtual void new_diseq_eh(euf::th_eq const& eq) {}
/**
\brief Parametric theories (e.g. Arrays) should implement this method.
*/
virtual bool is_shared(theory_var v) const { return false; }
};
class th_euf_solver : public th_solver {
@ -129,9 +131,15 @@ namespace euf {
euf::enode* e_internalize(expr* e) { internalize(e, m_is_redundant); return expr2enode(e); }
euf::enode* mk_enode(expr* e, bool suppress_args);
expr_ref mk_eq(expr* e1, expr* e2);
expr_ref mk_var_eq(theory_var v1, theory_var v2) { return mk_eq(var2expr(v1), var2expr(v2)); }
void rewrite(expr_ref& a);
virtual void push_core();
virtual void pop_core(unsigned n);
void force_push() { for (; m_num_scopes > 0; --m_num_scopes) push_core(); }
public:
th_euf_solver(euf::solver& ctx, euf::theory_id id);
virtual ~th_euf_solver() {}
@ -147,12 +155,9 @@ namespace euf {
trail_stack<euf::solver> & get_trail_stack();
bool is_attached_to_var(enode* n) const;
bool is_root(theory_var v) const { return var2enode(v)->is_root(); }
void push() override;
void pop(unsigned n) override;
void push() override { m_num_scopes++; }
void pop(unsigned n) override;
void lazy_push() { ++m_num_scopes; }
void force_push() { for (; m_num_scopes > 0; --m_num_scopes) push(); }
unsigned lazy_pop(unsigned n);
};

View file

@ -155,7 +155,10 @@ struct goal2sat::imp : public sat::sat_internalizer {
}
sat::bool_var add_var(bool is_ext, expr* n) {
auto v = m_solver.add_var(is_ext);
sat::bool_var v;
if (m_expr2var_replay && m_expr2var_replay->find(n, v))
return v;
v = m_solver.add_var(is_ext);
log_node(n);
log_def(v, n);
return v;
@ -298,18 +301,17 @@ struct goal2sat::imp : public sat::sat_internalizer {
}
}
bool process_cached(app * t, bool root, bool sign) {
sat::literal l;
if (m_cache.find(t, l)) {
if (sign)
l.neg();
if (root)
mk_root_clause(l);
else
m_result_stack.push_back(l);
return true;
}
return false;
bool process_cached(app* t, bool root, bool sign) {
sat::literal l = sat::null_literal;
if (!m_cache.find(t, l))
return false;
if (sign)
l.neg();
if (root)
mk_root_clause(l);
else
m_result_stack.push_back(l);
return true;
}
bool visit(expr * t, bool root, bool sign) {
@ -321,7 +323,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (process_cached(to_app(t), root, sign))
return true;
if (to_app(t)->get_family_id() != m.get_basic_family_id())
return convert_app(to_app(t), root, sign);
return convert_app(to_app(t), root, sign);
switch (to_app(t)->get_decl_kind()) {
case OP_NOT:
case OP_OR: