#include "smt/smt_implied_equalities.h"
#include "util/union_find.h"
#include "ast/ast_pp.h"
#include "ast/array_decl_plugin.h"
#include "util/uint_set.h"
#include "smt/smt_value_sort.h"
#include "model/model_smt2_pp.h"
#include "util/stopwatch.h"
#include "model/model.h"
#include "solver/solver.h"
namespace {
class get_implied_equalities_impl {
ast_manager& m;
solver& m_solver;
union_find_default_ctx m_df;
union_find<union_find_default_ctx> m_uf;
array_util m_array_util;
stopwatch m_stats_timer;
unsigned m_stats_calls;
stopwatch m_stats_val_eq_timer;
static stopwatch s_timer;
static stopwatch s_stats_val_eq_timer;
struct term_id {
expr_ref term;
unsigned id;
term_id(expr_ref t, unsigned id): term(t), id(id) {}
};
typedef vector<term_id> term_ids;
typedef obj_map<sort, term_ids> sort2term_ids;
void partition_terms(unsigned num_terms, expr* const* terms, sort2term_ids& termids) {
for (unsigned i = 0; i < num_terms; ++i) {
sort* s = terms[i]->get_sort();
term_ids& vec = termids.insert_if_not_there(s, term_ids());
vec.push_back(term_id(expr_ref(terms[i],m), i));
}
}
void get_implied_equalities_filter_basic(uint_set const& non_values, term_ids& terms) {
m_stats_timer.start();
uint_set root_indices;
for (unsigned j = 0; j < terms.size(); ++j) {
if (terms[j].id == m_uf.find(terms[j].id)) {
root_indices.insert(j);
}
}
uint_set::iterator it = non_values.begin(), end = non_values.end();
for (; it != end; ++it) {
unsigned i = *it;
expr* t = terms[i].term;
uint_set::iterator it2 = root_indices.begin(), end2 = root_indices.end();
bool found_root_value = false;
for (; it2 != end2; ++it2) {
unsigned j = *it2;
if (j == i) continue;
if (j < i && non_values.contains(j)) continue;
if (found_root_value && !non_values.contains(j)) continue;
expr* s = terms[j].term;
SASSERT(t->get_sort() == s->get_sort());
++m_stats_calls;
m_solver.push();
m_solver.assert_expr(m.mk_not(m.mk_eq(s, t)));
bool is_eq = l_false == m_solver.check_sat(0,nullptr);
m_solver.pop(1);
TRACE(get_implied_equalities, tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << (is_eq?"eq":"unrelated") << "\n";);
if (is_eq) {
m_uf.merge(terms[i].id, terms[j].id);
if (!non_values.contains(j)) {
found_root_value = true;
}
}
}
}
m_stats_timer.stop();
}
void get_implied_equalities_basic(term_ids& terms) {
for (unsigned i = 0; i < terms.size(); ++i) {
if (terms[i].id != m_uf.find(terms[i].id)) {
continue;
}
expr* t = terms[i].term;
for (unsigned j = 0; j < i; ++j) {
expr* s = terms[j].term;
SASSERT(t->get_sort() == s->get_sort());
++m_stats_calls;
m_stats_timer.start();
m_solver.push();
m_solver.assert_expr(m.mk_not(m.mk_eq(s, t)));
bool is_eq = l_false == m_solver.check_sat(0,nullptr);
m_solver.pop(1);
m_stats_timer.stop();
TRACE(get_implied_equalities, tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << (is_eq?"eq":"unrelated") << "\n";);
if (is_eq) {
m_uf.merge(terms[i].id, terms[j].id);
break;
}
}
}
}
void get_implied_equalities_model_based(model_ref& model, term_ids& terms) {
SASSERT(!terms.empty());
sort* srt = terms[0].term->get_sort();
if (m_array_util.is_array(srt)) {
m_solver.push();
unsigned arity = get_array_arity(srt);
expr_ref_vector args(m);
args.push_back(nullptr);
for (unsigned i = 0; i < arity; ++i) {
sort* srt_i = get_array_domain(srt, i);
expr* idx = m.mk_fresh_const("index", srt_i);
args.push_back(idx);
}
for (unsigned i = 0; i < terms.size(); ++i) {
args[0] = terms[i].term;
terms[i].term = m.mk_app(m_array_util.get_family_id(), OP_SELECT, 0, nullptr, args.size(), args.data());
}
assert_relevant(terms);
VERIFY(m_solver.check_sat(0,nullptr) != l_false);
model_ref model1;
m_solver.get_model(model1);
SASSERT(model1.get());
get_implied_equalities_model_based(model1, terms);
m_solver.pop(1);
return;
}
uint_set non_values;
if (!smt::is_value_sort(m, srt)) {
for (unsigned i = 0; i < terms.size(); ++i) {
non_values.insert(i);
}
get_implied_equalities_filter_basic(non_values, terms);
return;
}
expr_ref_vector vals(m);
expr_ref vl(m), eq(m);
obj_map<expr, unsigned_vector> vals_map;
m_stats_val_eq_timer.start();
s_stats_val_eq_timer.start();
params_ref p;
p.set_bool("produce_models", false);
m_solver.updt_params(p);
for (unsigned i = 0; i < terms.size(); ++i) {
expr* t = terms[i].term;
vl = (*model)(t);
TRACE(get_implied_equalities, tout << mk_pp(t, m) << " |-> " << mk_pp(vl, m) << "\n";);
reduce_value(model, vl);
if (!m.is_value(vl)) {
TRACE(get_implied_equalities, tout << "Not a value: " << mk_pp(vl, m) << "\n";);
non_values.insert(i);
continue;
}
vals.push_back(vl);
unsigned_vector& vec = vals_map.insert_if_not_there(vl, unsigned_vector());
bool found = false;
for (unsigned j = 0; !found && j < vec.size(); ++j) {
expr* s = terms[vec[j]].term;
m_solver.push();
m_solver.assert_expr(m.mk_not(m.mk_eq(t, s)));
lbool is_sat = m_solver.check_sat(0,nullptr);
m_solver.pop(1);
TRACE(get_implied_equalities, tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << is_sat << "\n";);
if (is_sat == l_false) {
found = true;
m_uf.merge(terms[i].id, terms[vec[j]].id);
}
}
if (!found) {
vec.push_back(i);
}
}
m_stats_val_eq_timer.stop();
s_stats_val_eq_timer.stop();
p.set_bool("produce_models", true);
m_solver.updt_params(p);
if (!non_values.empty()) {
TRACE(get_implied_equalities, model_smt2_pp(tout, m, *model, 0););
get_implied_equalities_filter_basic(non_values, terms);
}
}
void get_implied_equalities_core(model_ref& model, term_ids& terms) {
get_implied_equalities_model_based(model, terms);
}
void assert_relevant(unsigned num_terms, expr* const* terms) {
for (unsigned i = 0; i < num_terms; ++i) {
sort* srt = terms[i]->get_sort();
if (!m_array_util.is_array(srt)) {
m_solver.assert_expr(m.mk_app(m.mk_func_decl(symbol("Relevant!"), 1, &srt, m.mk_bool_sort()), terms[i]));
}
}
}
void assert_relevant(term_ids& terms) {
for (unsigned i = 0; i < terms.size(); ++i) {
expr* t = terms[i].term;
sort* srt = t->get_sort();
if (!m_array_util.is_array(srt)) {
m_solver.assert_expr(m.mk_app(m.mk_func_decl(symbol("Relevant!"), 1, &srt, m.mk_bool_sort()), t));
}
}
}
void reduce_value(model_ref& model, expr_ref& vl) {
expr* c, *e1, *e2;
while (m.is_ite(vl, c, e1, e2)) {
lbool r = reduce_cond(model, c);
switch(r) {
case l_true:
vl = e1;
break;
case l_false:
vl = e2;
break;
default:
return;
}
}
}
lbool reduce_cond(model_ref& model, expr* e) {
expr* e1 = nullptr, *e2 = nullptr;
if (m.is_eq(e, e1, e2) && m_array_util.is_as_array(e1) && m_array_util.is_as_array(e2)) {
if (e1 == e2) {
return l_true;
}
func_decl* f1 = m_array_util.get_as_array_func_decl(to_app(e1));
func_decl* f2 = m_array_util.get_as_array_func_decl(to_app(e2));
func_interp* fi1 = model->get_func_interp(f1);
func_interp* fi2 = model->get_func_interp(f2);
if (fi1 == fi2) {
return l_true;
}
unsigned n1 = fi1->num_entries();
for (unsigned i = 0; i < n1; ++i) {
func_entry const* h1 = fi1->get_entry(i);
for (unsigned j = 0; j < fi1->get_arity(); ++j) {
if (!m.is_value(h1->get_arg(j))) {
return l_undef;
}
}
func_entry* h2 = fi2->get_entry(h1->get_args());
if (h2 &&
h1->get_result() != h2->get_result() &&
m.is_value(h1->get_result()) &&
m.is_value(h2->get_result())) {
return l_false;
}
}
}
return l_undef;
}
public:
get_implied_equalities_impl(ast_manager& m, solver& s) : m(m), m_solver(s), m_uf(m_df), m_array_util(m), m_stats_calls(0) {}
lbool operator()(unsigned num_terms, expr* const* terms, unsigned* class_ids) {
params_ref p;
p.set_bool("produce_models", true);
m_solver.updt_params(p);
sort2term_ids termids;
stopwatch timer;
timer.start();
s_timer.start();
for (unsigned i = 0; i < num_terms; ++i) {
m_uf.mk_var();
}
m_solver.push();
assert_relevant(num_terms, terms);
lbool is_sat = m_solver.check_sat(0,nullptr);
if (is_sat != l_false) {
model_ref model;
m_solver.get_model(model);
SASSERT(model.get());
partition_terms(num_terms, terms, termids);
sort2term_ids::iterator it = termids.begin(), end = termids.end();
for (; it != end; ++it) {
term_ids& term_ids = it->m_value;
get_implied_equalities_core(model, term_ids);
for (unsigned i = 0; i < term_ids.size(); ++i) {
class_ids[term_ids[i].id] = m_uf.find(term_ids[i].id);
}
}
TRACE(get_implied_equalities,
for (unsigned i = 0; i < num_terms; ++i) {
tout << mk_pp(terms[i], m) << " |-> " << class_ids[i] << "\n";
});
}
m_solver.pop(1);
timer.stop();
s_timer.stop();
IF_VERBOSE(1, verbose_stream() << s_timer.get_seconds() << "\t" << num_terms << "\t"
<< timer.get_seconds() << "\t" << m_stats_calls << "\t"
<< m_stats_timer.get_seconds() << "\t"
<< m_stats_val_eq_timer.get_seconds() << "\t"
<< s_stats_val_eq_timer.get_seconds() << "\n";);
return is_sat;
}
};
stopwatch get_implied_equalities_impl::s_timer;
stopwatch get_implied_equalities_impl::s_stats_val_eq_timer;
}
namespace smt {
lbool implied_equalities(ast_manager& m, solver& solver, unsigned num_terms, expr* const* terms, unsigned* class_ids) {
get_implied_equalities_impl gi(m, solver);
return gi(num_terms, terms, class_ids);
}
}
#if 0#endif