3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-12 20:18:18 +00:00

running updates to bv_solver (#4674)

* na

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

* na

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

* na

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

* na

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

* na

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

* na

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

* na

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

* na

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

* dbg

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

* bv

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

* drat and fresh

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

* move ackerman functionality

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

* na

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

* debugability

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

* towards debugability

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

* missing file

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

* na

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

* na

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

* remove csp

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2020-09-07 20:35:32 -07:00 committed by GitHub
parent 4d1a2a2784
commit d02b0cde7a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 3060 additions and 3095 deletions

View file

@ -14,7 +14,6 @@ z3_add_component(ast
ast_translation.cpp ast_translation.cpp
ast_util.cpp ast_util.cpp
bv_decl_plugin.cpp bv_decl_plugin.cpp
csp_decl_plugin.cpp
datatype_decl_plugin.cpp datatype_decl_plugin.cpp
decl_collector.cpp decl_collector.cpp
display_dimacs.cpp display_dimacs.cpp

View file

@ -1342,6 +1342,17 @@ std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) {
return out; return out;
} }
std::ostream& operator<<(std::ostream& out, mk_ismt2_func const& p) {
smt2_pp_environment_dbg env(p.m);
format_ref r(fm(p.m));
unsigned len = 0;
r = env.pp_fdecl(p.m_fn, len);
params_ref pa;
pp(out, r.get(), p.m, pa);
return out;
}
std::ostream& operator<<(std::ostream& out, expr_ref const& e) { std::ostream& operator<<(std::ostream& out, expr_ref const& e) {
return out << mk_ismt2_pp(e.get(), e.get_manager()); return out << mk_ismt2_pp(e.get(), e.get_manager());
} }
@ -1388,6 +1399,8 @@ std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e) {
return out; return out;
} }
#ifdef Z3DEBUG #ifdef Z3DEBUG
void pp(expr const * n, ast_manager & m) { void pp(expr const * n, ast_manager & m) {
std::cout << mk_ismt2_pp(const_cast<expr*>(n), m) << std::endl; std::cout << mk_ismt2_pp(const_cast<expr*>(n), m) << std::endl;

View file

@ -122,6 +122,7 @@ struct mk_ismt2_pp {
mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr);
}; };
std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p); std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p);
std::ostream& operator<<(std::ostream& out, expr_ref const& e); std::ostream& operator<<(std::ostream& out, expr_ref const& e);
@ -136,3 +137,10 @@ std::ostream& operator<<(std::ostream& out, var_ref_vector const& e);
std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e); std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e);
std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e); std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e);
struct mk_ismt2_func {
func_decl* m_fn;
ast_manager &m;
mk_ismt2_func(func_decl* f, ast_manager& m): m_fn(f), m(m) {}
};
std::ostream& operator<<(std::ostream& out, mk_ismt2_func const& f);

View file

@ -131,12 +131,8 @@ void bv_decl_plugin::finalize() {
DEC_REF(m_int2bv); DEC_REF(m_int2bv);
DEC_REF(m_bv2int); DEC_REF(m_bv2int);
vector<ptr_vector<func_decl> >::iterator it = m_bit2bool.begin(); for (auto& ds : m_bit2bool)
vector<ptr_vector<func_decl> >::iterator end = m_bit2bool.end();
for (; it != end; ++it) {
ptr_vector<func_decl> & ds = *it;
DEC_REF(ds); DEC_REF(ds);
}
DEC_REF(m_mkbv); DEC_REF(m_mkbv);
} }
@ -829,6 +825,14 @@ bool bv_recognizers::is_bv2int(expr const* e, expr*& r) const {
return true; return true;
} }
bool bv_recognizers::is_bit2bool(expr* e, expr*& bv, unsigned& idx) const {
if (!is_bit2bool(e))
return false;
bv = to_app(e)->get_arg(0);
idx = to_app(e)->get_parameter(0).get_int();
return true;
}
bool bv_recognizers::mult_inverse(rational const & n, unsigned bv_size, rational & result) { bool bv_recognizers::mult_inverse(rational const & n, unsigned bv_size, rational & result) {
if (n.is_one()) { if (n.is_one()) {
result = n; result = n;

View file

@ -368,6 +368,9 @@ public:
MATCH_BINARY(is_bv_sdivi); MATCH_BINARY(is_bv_sdivi);
MATCH_BINARY(is_bv_udivi); MATCH_BINARY(is_bv_udivi);
MATCH_BINARY(is_bv_smodi); MATCH_BINARY(is_bv_smodi);
MATCH_UNARY(is_bit2bool);
MATCH_UNARY(is_int2bv);
bool is_bit2bool(expr* e, expr*& bv, unsigned& idx) const;
rational norm(rational const & val, unsigned bv_size, bool is_signed) const ; rational norm(rational const & val, unsigned bv_size, bool is_signed) const ;
rational norm(rational const & val, unsigned bv_size) const { return norm(val, bv_size, false); } rational norm(rational const & val, unsigned bv_size) const { return norm(val, bv_size, false); }
@ -432,6 +435,7 @@ public:
app * mk_bvsmul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_OVFL, n, m); } app * mk_bvsmul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_OVFL, n, m); }
app * mk_bvsmul_no_udfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_UDFL, n, m); } app * mk_bvsmul_no_udfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_UDFL, n, m); }
app * mk_bvumul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_NO_OVFL, n, m); } app * mk_bvumul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_NO_OVFL, n, m); }
app * mk_bit2bool(expr* e, unsigned idx) { parameter p(idx); return m_manager.mk_app(get_fid(), OP_BIT2BOOL, 1, &p, 1, &e); }
private: private:
void log_bv_from_exprs(app * r, unsigned n, expr* const* es) { void log_bv_from_exprs(app * r, unsigned n, expr* const* es) {

View file

@ -1,386 +0,0 @@
/*++
Copyright (c) 2018 Microsoft Corporation
Module Name:
csp_decl_plugin.h
Abstract:
Declarations used for a job-shop scheduling domain.
Author:
Nikolaj Bjorner (nbjorner) 2018-8-9
Revision History:
--*/
#include "ast/csp_decl_plugin.h"
#include "ast/arith_decl_plugin.h"
void csp_decl_plugin::set_manager(ast_manager* m, family_id fid) {
decl_plugin::set_manager(m, fid);
m_int_sort = nullptr;
m_alist_sort = nullptr;
m_job_sort = nullptr;
m_resource_sort = nullptr;
}
void csp_decl_plugin::init() {
if (!m_int_sort) {
m_int_sort = m_manager->mk_sort(m_manager->mk_family_id("arith"), INT_SORT);
m_alist_sort = m_manager->mk_sort(symbol("AList"), sort_info(m_family_id, ALIST_SORT));
m_job_sort = m_manager->mk_sort(symbol("Job"), sort_info(m_family_id, JOB_SORT));
m_resource_sort = m_manager->mk_sort(symbol("Resource"), sort_info(m_family_id, RESOURCE_SORT));
m_manager->inc_ref(m_int_sort);
m_manager->inc_ref(m_resource_sort);
m_manager->inc_ref(m_job_sort);
m_manager->inc_ref(m_alist_sort);
}
}
void csp_decl_plugin::finalize() {
m_manager->dec_ref(m_alist_sort);
m_manager->dec_ref(m_job_sort);
m_manager->dec_ref(m_resource_sort);
m_manager->dec_ref(m_int_sort);
}
sort * csp_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
init();
if (num_parameters != 0) {
m_manager->raise_exception("no parameters expected with job-shop sort");
}
switch (static_cast<js_sort_kind>(k)) {
case JOB_SORT: return m_job_sort;
case RESOURCE_SORT: return m_resource_sort;
case ALIST_SORT: return m_alist_sort;
default: UNREACHABLE(); return nullptr;
}
}
func_decl * csp_decl_plugin::mk_func_decl(
decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort *) {
init();
symbol name;
sort* rng = nullptr;
switch (static_cast<js_op_kind>(k)) {
case OP_JS_JOB:
check_arity(arity);
check_index1(num_parameters, parameters);
name = symbol("job");
rng = m_job_sort;
break;
case OP_JS_RESOURCE:
check_arity(arity);
check_index1(num_parameters, parameters);
name = symbol("resource");
rng = m_resource_sort;
break;
case OP_JS_RESOURCE_MAKESPAN:
if (arity != 1 || domain[0] != m_resource_sort) m_manager->raise_exception("makespan expects a resource argument");
name = symbol("makespan");
rng = m_int_sort;
break;
case OP_JS_START:
if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("start expects a job argument");
if (num_parameters > 0) m_manager->raise_exception("no parameters");
name = symbol("job-start");
rng = m_int_sort;
break;
case OP_JS_END:
if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("resource expects a job argument");
if (num_parameters > 0) m_manager->raise_exception("no parameters");
name = symbol("job-end");
rng = m_int_sort;
break;
case OP_JS_JOB2RESOURCE:
if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("job2resource expects a job argument");
if (num_parameters > 0) m_manager->raise_exception("no parameters");
name = symbol("job2resource");
rng = m_resource_sort;
break;
case OP_JS_MODEL:
// has no parameters
// all arguments are of sort alist
name = symbol("js-model");
rng = m_manager->mk_bool_sort();
break;
case OP_JS_JOB_RESOURCE:
if (arity != 6) m_manager->raise_exception("add-job-resource expects 6 arguments");
if (domain[0] != m_job_sort) m_manager->raise_exception("first argument of add-job-resource expects should be a job");
if (domain[1] != m_resource_sort) m_manager->raise_exception("second argument of add-job-resource expects should be a resource");
if (domain[2] != m_int_sort) m_manager->raise_exception("3rd argument of add-job-resource expects should be an integer");
if (domain[3] != m_int_sort) m_manager->raise_exception("4th argument of add-job-resource expects should be an integer");
if (domain[4] != m_int_sort) m_manager->raise_exception("5th argument of add-job-resource expects should be an integer");
if (domain[5] != m_alist_sort) m_manager->raise_exception("6th argument of add-job-resource should be a list of properties");
name = symbol("add-job-resource");
rng = m_alist_sort;
break;
case OP_JS_RESOURCE_AVAILABLE:
if (arity != 6) m_manager->raise_exception("add-resource-available expects 6 arguments");
if (domain[0] != m_resource_sort) m_manager->raise_exception("first argument of add-resource-available expects should be a resource");
if (domain[1] != m_int_sort) m_manager->raise_exception("2nd argument of add-resource-available expects should be an integer");
if (domain[2] != m_int_sort) m_manager->raise_exception("3rd argument of add-resource-available expects should be an integer");
if (domain[3] != m_int_sort) m_manager->raise_exception("4th argument of add-resource-available expects should be an integer");
if (domain[4] != m_int_sort) m_manager->raise_exception("5th argument of add-resource-available expects should be an integer");
if (domain[5] != m_alist_sort) m_manager->raise_exception("6th argument of add-resource-available should be a list of properties");
name = symbol("add-resource-available");
rng = m_alist_sort;
break;
case OP_JS_JOB_PREEMPTABLE:
if (arity != 1 || domain[0] != m_job_sort)
m_manager->raise_exception("set-preemptable expects one argument, which is a job");
name = symbol("set-preemptable");
rng = m_alist_sort;
break;
case OP_JS_PROPERTIES:
if (arity != 0) m_manager->raise_exception("js-properties takes no arguments");
for (unsigned i = 0; i < num_parameters; ++i) {
if (!parameters[i].is_symbol()) m_manager->raise_exception("js-properties expects a list of keyword parameters");
}
name = symbol("js-properties");
rng = m_alist_sort;
break;
case OP_JS_JOB_GOAL:
if (arity != 1 || domain[0] != m_job_sort)
m_manager->raise_exception("add-job-goal expects one argument, which is a job");
if (num_parameters != 2 || !parameters[0].is_symbol() || !parameters[1].is_int())
m_manager->raise_exception("add-job-goal expects one symbol and one integer parameter");
name = symbol("add-job-goal");
rng = m_alist_sort;
break;
case OP_JS_OBJECTIVE:
if (arity != 0)
m_manager->raise_exception("add-optimization-objective expects no arguments");
if (num_parameters != 1 || !parameters[0].is_symbol())
m_manager->raise_exception("add-optimization-objective expects one symbol parameter");
name = symbol("add-optimization-objective");
rng = m_alist_sort;
break;
default:
UNREACHABLE();
return nullptr;
}
return m_manager->mk_func_decl(name, arity, domain, rng, func_decl_info(m_family_id, k, num_parameters, parameters));
}
void csp_decl_plugin::check_arity(unsigned arity) {
if (arity > 0)
m_manager->raise_exception("csp variables use parameters only and take no arguments");
}
void csp_decl_plugin::check_index1(unsigned num_parameters, parameter const* ps) {
if (num_parameters != 1 || !ps[0].is_int())
m_manager->raise_exception("csp variable expects a single integer parameter");
}
void csp_decl_plugin::check_index2(unsigned num_parameters, parameter const* ps) {
if (num_parameters != 2 || !ps[0].is_int() || !ps[1].is_int())
m_manager->raise_exception("csp variable expects two integer parameters");
}
bool csp_decl_plugin::is_value(app * e) const {
return is_app_of(e, m_family_id, OP_JS_JOB) || is_app_of(e, m_family_id, OP_JS_RESOURCE);
}
void csp_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
if (logic == symbol("CSP")) {
op_names.push_back(builtin_name("job", OP_JS_JOB));
op_names.push_back(builtin_name("resource", OP_JS_RESOURCE));
op_names.push_back(builtin_name("makespan", OP_JS_RESOURCE_MAKESPAN));
op_names.push_back(builtin_name("job-start", OP_JS_START));
op_names.push_back(builtin_name("job-end", OP_JS_END));
op_names.push_back(builtin_name("job2resource", OP_JS_JOB2RESOURCE));
op_names.push_back(builtin_name("js-model", OP_JS_MODEL));
op_names.push_back(builtin_name("add-job-resource", OP_JS_JOB_RESOURCE));
op_names.push_back(builtin_name("add-resource-available", OP_JS_RESOURCE_AVAILABLE));
op_names.push_back(builtin_name("set-preemptable", OP_JS_JOB_PREEMPTABLE));
op_names.push_back(builtin_name("js-properties", OP_JS_PROPERTIES));
op_names.push_back(builtin_name("add-job-goal", OP_JS_JOB_GOAL));
op_names.push_back(builtin_name("add-optimization-objective", OP_JS_OBJECTIVE));
}
}
void csp_decl_plugin::get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) {
if (logic == symbol("CSP")) {
sort_names.push_back(builtin_name("Job", JOB_SORT));
sort_names.push_back(builtin_name("Resource", RESOURCE_SORT));
}
}
expr * csp_decl_plugin::get_some_value(sort * s) {
init();
parameter p(0);
if (is_sort_of(s, m_family_id, JOB_SORT))
return m_manager->mk_const(mk_func_decl(OP_JS_JOB, 1, &p, 0, nullptr, nullptr));
if (is_sort_of(s, m_family_id, RESOURCE_SORT))
return m_manager->mk_const(mk_func_decl(OP_JS_RESOURCE, 1, &p, 0, nullptr, nullptr));
UNREACHABLE();
return nullptr;
}
csp_util::csp_util(ast_manager& m): m(m) {
m_fid = m.mk_family_id("csp");
m_plugin = static_cast<csp_decl_plugin*>(m.get_plugin(m_fid));
}
sort* csp_util::mk_job_sort() {
return m_plugin->mk_job_sort();
}
sort* csp_util::mk_resource_sort() {
return m_plugin->mk_resource_sort();
}
app* csp_util::mk_job(unsigned j) {
parameter p(j);
return m.mk_const(m.mk_func_decl(m_fid, OP_JS_JOB, 1, &p, 0, (sort*const*)nullptr, nullptr));
}
unsigned csp_util::job2id(expr* j) {
if (is_app_of(j, m_fid, OP_JS_JOB)) {
return to_app(j)->get_decl()->get_parameter(0).get_int();
}
SASSERT(is_app_of(j, m_fid, OP_JS_START) ||
is_app_of(j, m_fid, OP_JS_END) ||
is_app_of(j, m_fid, OP_JS_JOB2RESOURCE));
return job2id(to_app(j)->get_arg(0));
}
app* csp_util::mk_resource(unsigned r) {
parameter p(r);
return m.mk_const(m.mk_func_decl(m_fid, OP_JS_RESOURCE, 1, &p, 0, (sort*const*)nullptr, nullptr));
}
unsigned csp_util::resource2id(expr* r) {
SASSERT(is_app_of(r, m_fid, OP_JS_RESOURCE));
return to_app(r)->get_decl()->get_parameter(0).get_int();
}
app* csp_util::mk_start(unsigned j) {
app_ref job(mk_job(j), m);
sort* js = m.get_sort(job);
return m.mk_app(m.mk_func_decl(m_fid, OP_JS_START, 0, nullptr, 1, &js, nullptr), job);
}
app* csp_util::mk_end(unsigned j) {
app_ref job(mk_job(j), m);
sort* js = m.get_sort(job);
return m.mk_app(m.mk_func_decl(m_fid, OP_JS_END, 0, nullptr, 1, &js, nullptr), job);
}
app* csp_util::mk_job2resource(unsigned j) {
app_ref job(mk_job(j), m);
sort* js = m.get_sort(job);
return m.mk_app(m.mk_func_decl(m_fid, OP_JS_JOB2RESOURCE, 0, nullptr, 1, &js, nullptr), job);
}
app* csp_util::mk_makespan(unsigned r) {
app_ref resource(mk_resource(r), m);
sort* rs = m.get_sort(resource);
return m.mk_app(m.mk_func_decl(m_fid, OP_JS_RESOURCE_MAKESPAN, 0, nullptr, 1, &rs, nullptr), resource);
}
bool csp_util::is_resource(expr* e, unsigned& r) {
return is_app_of(e, m_fid, OP_JS_RESOURCE) && (r = resource2id(e), true);
}
bool csp_util::is_makespan(expr * e, unsigned& r) {
return is_app_of(e, m_fid, OP_JS_RESOURCE_MAKESPAN) && is_resource(to_app(e)->get_arg(0), r);
}
bool csp_util::is_job(expr* e, unsigned& j) {
return is_app_of(e, m_fid, OP_JS_JOB) && (j = job2id(e), true);
}
bool csp_util::is_job2resource(expr* e, unsigned& j) {
return is_app_of(e, m_fid, OP_JS_JOB2RESOURCE) && (j = job2id(e), true);
}
bool csp_util::is_add_resource_available(expr * e, expr *& res, unsigned& loadpct, unsigned& cap_time, uint64_t& start, uint64_t& end, svector<symbol>& properties) {
if (!is_app_of(e, m_fid, OP_JS_RESOURCE_AVAILABLE)) return false;
res = to_app(e)->get_arg(0);
arith_util a(m);
rational r;
if (!a.is_numeral(to_app(e)->get_arg(1), r) || !r.is_unsigned()) return false;
loadpct = r.get_unsigned();
if (!a.is_numeral(to_app(e)->get_arg(2), r) || !r.is_unsigned()) return false;
cap_time = r.get_unsigned();
if (!a.is_numeral(to_app(e)->get_arg(3), r) || !r.is_uint64()) return false;
start = r.get_uint64();
if (!a.is_numeral(to_app(e)->get_arg(4), r) || !r.is_uint64()) return false;
end = r.get_uint64();
if (!is_js_properties(to_app(e)->get_arg(5), properties)) return false;
return true;
}
bool csp_util::is_add_job_resource(expr * e, expr *& job, expr*& res, unsigned& loadpct, uint64_t& capacity, uint64_t& end, svector<symbol>& properties) {
if (!is_app_of(e, m_fid, OP_JS_JOB_RESOURCE)) return false;
job = to_app(e)->get_arg(0);
res = to_app(e)->get_arg(1);
arith_util a(m);
rational r;
if (!a.is_numeral(to_app(e)->get_arg(2), r) || !r.is_unsigned()) return false;
loadpct = r.get_unsigned();
if (!a.is_numeral(to_app(e)->get_arg(3), r) || !r.is_uint64()) return false;
capacity = r.get_uint64();
if (!a.is_numeral(to_app(e)->get_arg(4), r) || !r.is_uint64()) return false;
end = r.get_uint64();
if (!is_js_properties(to_app(e)->get_arg(5), properties)) return false;
return true;
}
bool csp_util::is_set_preemptable(expr* e, expr *& job) {
if (!is_app_of(e, m_fid, OP_JS_JOB_PREEMPTABLE)) return false;
job = to_app(e)->get_arg(0);
return true;
}
bool csp_util::is_js_properties(expr* e, svector<symbol>& properties) {
if (!is_app_of(e, m_fid, OP_JS_PROPERTIES))
return false;
unsigned sz = to_app(e)->get_decl()->get_num_parameters();
for (unsigned i = 0; i < sz; ++i) {
properties.push_back(to_app(e)->get_decl()->get_parameter(i).get_symbol());
}
return true;
}
bool csp_util::is_job_goal(expr* e, js_job_goal& goal, unsigned& level, expr*& job) {
if (!is_app_of(e, m_fid, OP_JS_JOB_GOAL))
return false;
SASSERT(2 == to_app(e)->get_decl()->get_num_parameters());
SASSERT(1 == to_app(e)->get_num_args());
symbol g = to_app(e)->get_decl()->get_parameter(0).get_symbol();
level = to_app(e)->get_decl()->get_parameter(1).get_int();
if (g == ":earliest-end-time" || g == "earliest-end-time")
goal = JS_JOB_GOAL_EARLIEST_END_TIME;
else if (g == ":latest-start-time" || g == "latest-start-time")
goal = JS_JOB_GOAL_LATEST_START_TIME;
else
return false;
job = to_app(e)->get_arg(0);
return true;
}
bool csp_util::is_objective(expr* e, js_optimization_objective& objective) {
if (!is_app_of(e, m_fid, OP_JS_OBJECTIVE))
return false;
SASSERT(1 == to_app(e)->get_decl()->get_num_parameters());
symbol obj = to_app(e)->get_decl()->get_parameter(0).get_symbol();
if (obj == ":duration" || obj == "duration")
objective = JS_OBJECTIVE_DURATION;
else if (obj == ":priority" || obj == "priority")
objective = JS_OBJECTIVE_PRIORITY;
else
return false;
return true;
}

View file

@ -1,163 +0,0 @@
/*++
Copyright (c) 2018 Microsoft Corporation
Module Name:
csp_decl_plugin.h
Abstract:
Declarations used for a job-shop scheduling domain.
The job-shop domain comprises of constants job(j), resource(r)
It finds values to variables:
- start(j), end(j), job2resource(j)
It assumes a background of:
- resources : Job -> Resource -> Int * LoadPct - time to run job j on resource r assuming LoadPct
- runtime : Job -> Int - time to run job j if not associated with any resource
- capacity : Resource -> Int -> LoadPct - capacity of resource r at time t, given as sequence of time intervals
// assume each job has at least one resource associated with it.
// introduce a dummy resource if needed.
// Theory:
end(j) - start(j) = time-to-execute(j)
time-to-execute(j) := time-to-execute(j, resource(j)) otherwise
time-to-execute(j, r) := (T - start(j))
where capacity(j,r) = sum_{t = start(j)}^{T} load(loadpct(j,r), r, t)
capacity(j, r) := cap where (cap, loadpct) = resources j r
loadpct(j, r) := loadpct where (cap, loadpct) = resources j r
load(loadpct, r, t) := min(capacity r t, loadpct) / loadpct
capacity(r, t) >= sum_{j | job-on-resource(j, r, t) } min(capacity r t, loadpct(j, r))
// Macros:
job-on-resource(j, r) := r = resource(j);
job-on-resource(j, r, t) := (job-on-resource(j, r) & start(j) <= t <= end(j));
start_min(j, t) := start(j) >= t;
end_max(j, t) := end(j) <= t;
job_link(j1, j2, startstart, hard) := start(j1) = start(j2);
job_link(j1, j2, startstart, soft) := start(j1) <= start(j2);
job_link(j1, j2, endend, hard) := end(j1) = end(j2);
job_link(j1, j2, endend, soft) := end(j2) <= end(j1);
job_link(j1, j2, endstart, hard) := end(j1) = start(j2);
job_link(j1, j2, endstart, soft) := end(j2) <= start(j1);
job_link(j1, j2, startend, hard) := end(j2) = start(j1);
job_link(j1, j2, startend, soft) := end(j1) <= start(j2);
job_delay(j1, j2, t) := end(j1) + t <= end(j2);
job_on_same_resource(j1, j2) := resource(j1) = resource(j2);
job_not_on_same_resource(j1, j2) := resource(j1) != resource(j2);
job_time_intersect(j1, j2) := start(j1) <= end(j2) <= end(j1) || start(j2) <= end(j1) <= end(j2);
job-on-resource(j, r, t) => job-property(j) = null or job_property(j) in working_time_property(r, t);
Author:
Nikolaj Bjorner (nbjorner) 2018-8-9
Revision History:
--*/
#pragma once
#include "ast/ast.h"
enum js_sort_kind {
JOB_SORT,
RESOURCE_SORT,
ALIST_SORT
};
enum js_op_kind {
OP_JS_JOB, // value of type job
OP_JS_RESOURCE, // value of type resource
OP_JS_RESOURCE_MAKESPAN, // makespan of resource: the minimal resource time required for assigned jobs.
OP_JS_START, // start time of a job
OP_JS_END, // end time of a job
OP_JS_JOB2RESOURCE, // resource associated with job
OP_JS_MODEL, // jobscheduler model
OP_JS_JOB_RESOURCE, // model declaration for job assignment to resource
OP_JS_JOB_PREEMPTABLE, // model declaration for whether job is pre-emptable
OP_JS_RESOURCE_AVAILABLE, // model declaration for availability intervals of resource
OP_JS_PROPERTIES, // model declaration of a set of properties. Each property is a keyword.
OP_JS_JOB_GOAL, // job goal objective :earliest-end-time or :latest-start-time
OP_JS_OBJECTIVE // duration or completion-time
};
enum js_job_goal {
JS_JOB_GOAL_EARLIEST_END_TIME,
JS_JOB_GOAL_LATEST_START_TIME
};
enum js_optimization_objective {
JS_OBJECTIVE_DURATION,
JS_OBJECTIVE_PRIORITY
};
class csp_decl_plugin : public decl_plugin {
void init();
public:
csp_decl_plugin() {}
~csp_decl_plugin() override {}
void finalize() override;
void set_manager(ast_manager* m, family_id fid) override;
decl_plugin * mk_fresh() override { return alloc(csp_decl_plugin); }
sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override;
func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) override;
bool is_value(app * e) const override;
bool is_unique_value(app * e) const override { return is_value(e); }
void get_op_names(svector<builtin_name> & op_names, symbol const & logic) override;
void get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) override;
expr * get_some_value(sort * s) override;
sort * mk_job_sort() const { return m_job_sort; }
sort * mk_resource_sort() const { return m_resource_sort; }
sort * mk_alist_sort() const { return m_alist_sort; }
private:
sort* m_job_sort;
sort* m_resource_sort;
sort* m_alist_sort;
sort* m_int_sort;
void check_arity(unsigned arity);
void check_index1(unsigned n, parameter const* ps);
void check_index2(unsigned n, parameter const* ps);
};
class csp_util {
ast_manager& m;
family_id m_fid;
csp_decl_plugin* m_plugin;
public:
csp_util(ast_manager& m);
sort* mk_job_sort();
sort* mk_resource_sort();
app* mk_job(unsigned j);
app* mk_resource(unsigned r);
app* mk_start(unsigned j);
app* mk_end(unsigned j);
app* mk_job2resource(unsigned j);
app* mk_makespan(unsigned r);
bool is_job(expr* e, unsigned& j);
bool is_job2resource(expr* e, unsigned& j);
bool is_resource(expr* e, unsigned& r);
bool is_makespan(expr* e, unsigned& r);
bool is_add_resource_available(expr * e, expr *& res, unsigned& loadpct, unsigned& cap_time, uint64_t& start, uint64_t& end, svector<symbol>& properites);
bool is_add_job_resource(expr * e, expr *& job, expr*& res, unsigned& loadpct, uint64_t& capacity, uint64_t& finite_capacity_end, svector<symbol>& properites);
bool is_set_preemptable(expr* e, expr *& job);
bool is_model(expr* e) const { return is_app_of(e, m_fid, OP_JS_MODEL); }
bool is_js_properties(expr* e, svector<symbol>& properties);
bool is_job_goal(expr* e, js_job_goal& goal, unsigned& level, expr*& job);
bool is_objective(expr* e, js_optimization_objective& objective);
private:
unsigned job2id(expr* j);
unsigned resource2id(expr* r);
};

View file

@ -17,52 +17,11 @@ Author:
#include "ast/euf/euf_egraph.h" #include "ast/euf/euf_egraph.h"
#include "ast/ast_pp.h" #include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/ast_translation.h" #include "ast/ast_translation.h"
namespace euf { namespace euf {
/**
\brief Trail for add_th_var
*/
class add_th_var_trail : public trail<egraph> {
enode * m_enode;
theory_id m_th_id;
public:
add_th_var_trail(enode * n, theory_id th_id):
m_enode(n),
m_th_id(th_id) {
}
void undo(egraph & ctx) override {
theory_var v = m_enode->get_th_var(m_th_id);
SASSERT(v != null_theory_var);
m_enode->del_th_var(m_th_id);
enode * root = m_enode->get_root();
if (root != m_enode && root->get_th_var(m_th_id) == v)
root->del_th_var(m_th_id);
}
};
/**
\brief Trail for replace_th_var
*/
class replace_th_var_trail : public trail<egraph> {
enode * m_enode;
unsigned m_th_id:8;
unsigned m_old_th_var:24;
public:
replace_th_var_trail(enode * n, theory_id th_id, theory_var old_var):
m_enode(n),
m_th_id(th_id),
m_old_th_var(old_var) {
}
void undo(egraph & ctx) override {
SASSERT(m_enode->get_th_var(m_th_id) != null_theory_var);
m_enode->replace_th_var(m_old_th_var, m_th_id);
}
};
void egraph::undo_eq(enode* r1, enode* n1, unsigned r2_num_parents) { void egraph::undo_eq(enode* r1, enode* n1, unsigned r2_num_parents) {
enode* r2 = r1->get_root(); enode* r2 = r1->get_root();
r2->dec_class_size(r1->class_size()); r2->dec_class_size(r1->class_size());
@ -82,6 +41,9 @@ namespace euf {
enode* n = enode::mk(m_region, f, num_args, args); enode* n = enode::mk(m_region, f, num_args, args);
m_nodes.push_back(n); m_nodes.push_back(n);
m_exprs.push_back(f); m_exprs.push_back(f);
push_node(n);
for (unsigned i = 0; i < num_args; ++i)
set_merge_enabled(args[i], true);
return n; return n;
} }
@ -104,8 +66,7 @@ namespace euf {
void egraph::reinsert_equality(enode* p) { void egraph::reinsert_equality(enode* p) {
SASSERT(is_equality(p)); SASSERT(is_equality(p));
if (p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) { if (p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) {
m_new_lits.push_back(enode_bool_pair(p, true)); add_literal(p, true);
++m_stats.m_num_eqs;
} }
} }
@ -114,19 +75,14 @@ namespace euf {
} }
void egraph::force_push() { void egraph::force_push() {
if (m_num_scopes == 0)
return;
for (; m_num_scopes > 0; --m_num_scopes) { for (; m_num_scopes > 0; --m_num_scopes) {
scope s; m_scopes.push_back(m_updates.size());
s.m_inconsistent = m_inconsistent;
s.m_num_eqs = m_eqs.size();
s.m_num_nodes = m_nodes.size();
s.m_trail_sz = m_trail.size();
s.m_new_lits_sz = m_new_lits.size();
s.m_new_th_eqs_sz = m_new_th_eqs.size();
s.m_new_lits_qhead = m_new_lits_qhead;
s.m_new_th_eqs_qhead = m_new_th_eqs_qhead;
m_scopes.push_back(s);
m_region.push_scope(); m_region.push_scope();
} }
m_updates.push_back(update_record(m_new_lits_qhead, update_record::new_lits_qhead()));
m_updates.push_back(update_record(m_new_th_eqs_qhead, update_record::new_th_eq_qhead()));
} }
void egraph::update_children(enode* n) { void egraph::update_children(enode* n) {
@ -147,6 +103,7 @@ namespace euf {
return n; return n;
if (is_equality(n)) { if (is_equality(n)) {
update_children(n); update_children(n);
reinsert_equality(n);
return n; return n;
} }
enode_bool_pair p = m_table.insert(n); enode_bool_pair p = m_table.insert(n);
@ -170,6 +127,18 @@ namespace euf {
n->m_parents.finalize(); n->m_parents.finalize();
} }
void egraph::add_th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r) {
m_new_th_eqs.push_back(th_eq(id, v1, v2, c, r));
m_updates.push_back(update_record(update_record::new_th_eq()));
++m_stats.m_num_th_eqs;
}
void egraph::add_literal(enode* n, bool is_eq) {
m_new_lits.push_back(enode_bool_pair(n, is_eq));
m_updates.push_back(update_record(update_record::new_lit()));
if (is_eq) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits;
}
void egraph::add_th_var(enode* n, theory_var v, theory_id id) { void egraph::add_th_var(enode* n, theory_var v, theory_id id) {
force_push(); force_push();
theory_var w = n->get_th_var(id); theory_var w = n->get_th_var(id);
@ -177,21 +146,37 @@ namespace euf {
if (w == null_theory_var) { if (w == null_theory_var) {
n->add_th_var(v, id, m_region); n->add_th_var(v, id, m_region);
m_trail.push_back(new (m_region) add_th_var_trail(n, id)); m_updates.push_back(update_record(n, id, update_record::add_th_var()));
if (r != n) { if (r != n) {
theory_var u = r->get_th_var(id); theory_var u = r->get_th_var(id);
if (u == null_theory_var) if (u == null_theory_var)
r->add_th_var(v, id, m_region); r->add_th_var(v, id, m_region);
else else
m_new_th_eqs.push_back(th_eq(id, v, u, n, r)); add_th_eq(id, v, u, n, r);
} }
} }
else { else {
theory_var u = r->get_th_var(id); theory_var u = r->get_th_var(id);
SASSERT(u != v && u != null_theory_var); SASSERT(u != v && u != null_theory_var);
n->replace_th_var(v, id); n->replace_th_var(v, id);
m_trail.push_back(new (m_region) replace_th_var_trail(n, id, u)); m_updates.push_back(update_record(n, id, u, update_record::replace_th_var()));
m_new_th_eqs.push_back(th_eq(id, v, u, n, r)); add_th_eq(id, v, u, n, r);
}
}
void egraph::undo_add_th_var(enode* n, theory_id tid) {
theory_var v = n->get_th_var(tid);
SASSERT(v != null_theory_var);
n->del_th_var(tid);
enode* root = n->get_root();
if (root != n && root->get_th_var(tid) == v)
root->del_th_var(tid);
}
void egraph::set_merge_enabled(enode* n, bool enable_merge) {
if (enable_merge != n->merge_enabled()) {
m_updates.push_back(update_record(n, update_record::toggle_merge()));
n->set_merge_enabled(enable_merge);
} }
} }
@ -202,29 +187,59 @@ namespace euf {
} }
num_scopes -= m_num_scopes; num_scopes -= m_num_scopes;
unsigned old_lim = m_scopes.size() - num_scopes; unsigned old_lim = m_scopes.size() - num_scopes;
scope s = m_scopes[old_lim]; unsigned num_updates = m_scopes[old_lim];
for (unsigned i = m_eqs.size(); i-- > s.m_num_eqs; ) { auto undo_node = [&](enode* n) {
auto const& p = m_eqs[i];
undo_eq(p.r1, p.n1, p.r2_num_parents);
}
for (unsigned i = m_nodes.size(); i-- > s.m_num_nodes; ) {
enode* n = m_nodes[i];
if (n->num_args() > 1) if (n->num_args() > 1)
m_table.erase(n); m_table.erase(n);
m_expr2enode[n->get_owner_id()] = nullptr; m_expr2enode[n->get_owner_id()] = nullptr;
n->~enode(); n->~enode();
m_nodes.pop_back();
m_exprs.pop_back();
};
for (unsigned i = m_updates.size(); i-- > num_updates; ) {
auto const& p = m_updates[i];
switch (p.tag) {
case update_record::tag_t::is_add_node:
undo_node(p.r1);
break;
case update_record::tag_t::is_toggle_merge:
p.r1->set_merge_enabled(!p.r1->merge_enabled());
break;
case update_record::tag_t::is_set_parent:
undo_eq(p.r1, p.n1, p.r2_num_parents);
break;
case update_record::tag_t::is_add_th_var:
undo_add_th_var(p.r1, p.r2_num_parents);
break;
case update_record::tag_t::is_replace_th_var:
SASSERT(p.r1->get_th_var(p.m_th_id) != null_theory_var);
p.r1->replace_th_var(p.m_old_th_var, p.m_th_id);
break;
case update_record::tag_t::is_new_lit:
m_new_lits.pop_back();
break;
case update_record::tag_t::is_new_th_eq:
m_new_th_eqs.pop_back();
break;
case update_record::tag_t::is_new_th_eq_qhead:
m_new_th_eqs_qhead = p.qhead;
break;
case update_record::tag_t::is_new_lits_qhead:
m_new_lits_qhead = p.qhead;
break;
case update_record::tag_t::is_inconsistent:
m_inconsistent = p.m_inconsistent;
break;
default:
UNREACHABLE();
break;
} }
undo_trail_stack<egraph>(*this, m_trail, s.m_trail_sz); }
m_inconsistent = s.m_inconsistent;
m_new_lits_qhead = s.m_new_lits_qhead; m_updates.shrink(num_updates);
m_new_th_eqs_qhead = s.m_new_th_eqs_qhead;
m_eqs.shrink(s.m_num_eqs);
m_nodes.shrink(s.m_num_nodes);
m_exprs.shrink(s.m_num_nodes);
m_new_lits.shrink(s.m_new_lits_sz);
m_new_th_eqs.shrink(s.m_new_th_eqs_sz);
m_scopes.shrink(old_lim); m_scopes.shrink(old_lim);
m_region.pop_scope(num_scopes); m_region.pop_scope(num_scopes);
m_worklist.reset();
} }
void egraph::merge(enode* n1, enode* n2, justification j) { void egraph::merge(enode* n1, enode* n2, justification j) {
@ -245,8 +260,7 @@ namespace euf {
std::swap(n1, n2); std::swap(n1, n2);
} }
if ((m.is_true(r2->get_owner()) || m.is_false(r2->get_owner())) && j.is_congruence()) { if ((m.is_true(r2->get_owner()) || m.is_false(r2->get_owner())) && j.is_congruence()) {
m_new_lits.push_back(enode_bool_pair(n1, false)); add_literal(n1, false);
++m_stats.m_num_lits;
} }
for (enode* p : enode_parents(n1)) for (enode* p : enode_parents(n1))
m_table.erase(p); m_table.erase(p);
@ -270,11 +284,11 @@ namespace euf {
theory_var v = root->get_th_var(id); theory_var v = root->get_th_var(id);
if (v == null_theory_var) { if (v == null_theory_var) {
root->add_th_var(iv.get_var(), id, m_region); root->add_th_var(iv.get_var(), id, m_region);
m_trail.push_back(new (m_region) add_th_var_trail(root, id)); m_updates.push_back(update_record(root, id, update_record::add_th_var()));
} }
else { else {
SASSERT(v != iv.get_var()); SASSERT(v != iv.get_var());
m_new_th_eqs.push_back(th_eq(id, v, iv.get_var(), n, root)); add_th_eq(id, v, iv.get_var(), n, root);
} }
} }
} }
@ -308,6 +322,7 @@ namespace euf {
if (m_inconsistent) if (m_inconsistent)
return; return;
m_inconsistent = true; m_inconsistent = true;
m_updates.push_back(update_record(false, update_record::inconsistent()));
m_n1 = n1; m_n1 = n1;
m_n2 = n2; m_n2 = n2;
m_justification = j; m_justification = j;
@ -387,10 +402,19 @@ namespace euf {
} }
} }
void egraph::begin_explain() {
SASSERT(m_todo.empty());
}
void egraph::end_explain() {
for (enode* n : m_todo)
n->unmark1();
m_todo.reset();
}
template <typename T> template <typename T>
void egraph::explain(ptr_vector<T>& justifications) { void egraph::explain(ptr_vector<T>& justifications) {
SASSERT(m_inconsistent); SASSERT(m_inconsistent);
SASSERT(m_todo.empty());
push_todo(m_n1); push_todo(m_n1);
push_todo(m_n2); push_todo(m_n2);
explain_eq(justifications, m_n1, m_n2, m_justification); explain_eq(justifications, m_n1, m_n2, m_justification);
@ -399,7 +423,6 @@ namespace euf {
template <typename T> template <typename T>
void egraph::explain_eq(ptr_vector<T>& justifications, enode* a, enode* b) { void egraph::explain_eq(ptr_vector<T>& justifications, enode* a, enode* b) {
SASSERT(m_todo.empty());
SASSERT(a->get_root() == b->get_root()); SASSERT(a->get_root() == b->get_root());
enode* lca = find_lca(a, b); enode* lca = find_lca(a, b);
push_to_lca(a, lca); push_to_lca(a, lca);
@ -418,9 +441,6 @@ namespace euf {
explain_eq(justifications, n, n->m_target, n->m_justification); explain_eq(justifications, n, n->m_target, n->m_justification);
} }
} }
for (enode* n : m_todo)
n->unmark1();
m_todo.reset();
} }
void egraph::invariant() { void egraph::invariant() {
@ -429,25 +449,29 @@ namespace euf {
} }
std::ostream& egraph::display(std::ostream& out, unsigned max_args, enode* n) const { std::ostream& egraph::display(std::ostream& out, unsigned max_args, enode* n) const {
out << std::setw(5) out << n->get_owner_id() << " := ";
<< n->get_owner_id() << " := ";
if (!n->is_root()) if (!n->is_root())
out << "[" << n->get_root()->get_owner_id() << "] "; out << "[" << n->get_root()->get_owner_id() << "] ";
expr* f = n->get_owner(); expr* f = n->get_owner();
if (is_app(f)) if (is_app(f))
out << to_app(f)->get_decl()->get_name() << " "; out << mk_bounded_pp(f, m, 1);
else if (is_quantifier(f)) else if (is_quantifier(f))
out << "q "; out << "q:" << f->get_id();
else else
out << "v "; out << "v:" << f->get_id();
for (enode* arg : enode_args(n)) out << "\n";
out << arg->get_owner_id() << " "; if (!n->m_parents.empty()) {
for (unsigned i = n->num_args(); i < max_args; ++i)
out << " "; out << " ";
out << "\t";
for (enode* p : enode_parents(n)) for (enode* p : enode_parents(n))
out << p->get_owner_id() << " "; out << p->get_owner_id() << " ";
out << "\n"; out << "\n";
}
if (n->has_th_vars()) {
out << " ";
for (auto v : enode_th_vars(n))
out << v.get_id() << ":" << v.get_var() << " ";
out << "\n";
}
return out; return out;
} }
@ -464,8 +488,9 @@ namespace euf {
void egraph::collect_statistics(statistics& st) const { void egraph::collect_statistics(statistics& st) const {
st.update("euf merge", m_stats.m_num_merge); st.update("euf merge", m_stats.m_num_merge);
st.update("euf conflicts", m_stats.m_num_conflicts); st.update("euf conflicts", m_stats.m_num_conflicts);
st.update("euf eq prop", m_stats.m_num_eqs); st.update("euf equality propagations", m_stats.m_num_eqs);
st.update("euf lit prop", m_stats.m_num_lits); st.update("euf theory equality propagations", m_stats.m_num_th_eqs);
st.update("euf literal propagations", m_stats.m_num_lits);
} }
void egraph::copy_from(egraph const& src, std::function<void*(void*)>& copy_justification) { void egraph::copy_from(egraph const& src, std::function<void*(void*)>& copy_justification) {
@ -504,7 +529,7 @@ template void euf::egraph::explain(ptr_vector<int>& justifications);
template void euf::egraph::explain_todo(ptr_vector<int>& justifications); template void euf::egraph::explain_todo(ptr_vector<int>& justifications);
template void euf::egraph::explain_eq(ptr_vector<int>& justifications, enode* a, enode* b); template void euf::egraph::explain_eq(ptr_vector<int>& justifications, enode* a, enode* b);
template void euf::egraph::explain(ptr_vector<unsigned>& justifications); template void euf::egraph::explain(ptr_vector<size_t>& justifications);
template void euf::egraph::explain_todo(ptr_vector<unsigned>& justifications); template void euf::egraph::explain_todo(ptr_vector<size_t>& justifications);
template void euf::egraph::explain_eq(ptr_vector<unsigned>& justifications, enode* a, enode* b); template void euf::egraph::explain_eq(ptr_vector<size_t>& justifications, enode* a, enode* b);

View file

@ -31,14 +31,6 @@ Notes:
namespace euf { namespace euf {
struct add_eq_record {
enode* r1;
enode* n1;
unsigned r2_num_parents;
add_eq_record(enode* r1, enode* n1, unsigned r2_num_parents):
r1(r1), n1(n1), r2_num_parents(r2_num_parents) {}
};
/*** /***
\brief store derived theory equalities. \brief store derived theory equalities.
Theory 'id' is notified with the equality of theory variables v1, v2 Theory 'id' is notified with the equality of theory variables v1, v2
@ -58,31 +50,66 @@ namespace euf {
class egraph { class egraph {
typedef ptr_vector<trail<egraph> > trail_stack; typedef ptr_vector<trail<egraph> > trail_stack;
struct scope {
bool m_inconsistent;
unsigned m_num_eqs;
unsigned m_num_nodes;
unsigned m_trail_sz;
unsigned m_new_lits_sz;
unsigned m_new_th_eqs_sz;
unsigned m_new_lits_qhead;
unsigned m_new_th_eqs_qhead;
};
struct stats { struct stats {
unsigned m_num_merge; unsigned m_num_merge;
unsigned m_num_th_eqs;
unsigned m_num_lits; unsigned m_num_lits;
unsigned m_num_eqs; unsigned m_num_eqs;
unsigned m_num_conflicts; unsigned m_num_conflicts;
stats() { reset(); } stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); } void reset() { memset(this, 0, sizeof(*this)); }
}; };
struct update_record {
struct toggle_merge {};
struct add_th_var {};
struct replace_th_var {};
struct new_lit {};
struct new_th_eq {};
struct new_th_eq_qhead {};
struct new_lits_qhead {};
struct inconsistent {};
enum tag_t { is_set_parent, is_add_node, is_toggle_merge,
is_add_th_var, is_replace_th_var, is_new_lit, is_new_th_eq,
is_new_th_eq_qhead, is_new_lits_qhead, is_inconsistent };
tag_t tag;
enode* r1;
enode* n1;
union {
unsigned r2_num_parents;
struct {
unsigned m_th_id : 8;
unsigned m_old_th_var : 24;
};
unsigned qhead;
bool m_inconsistent;
};
update_record(enode* r1, enode* n1, unsigned r2_num_parents) :
tag(tag_t::is_set_parent), r1(r1), n1(n1), r2_num_parents(r2_num_parents) {}
update_record(enode* n) :
tag(tag_t::is_add_node), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
update_record(enode* n, toggle_merge) :
tag(tag_t::is_toggle_merge), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
update_record(enode* n, unsigned id, add_th_var) :
tag(tag_t::is_add_th_var), r1(n), n1(nullptr), r2_num_parents(id) {}
update_record(enode* n, theory_id id, theory_var v, replace_th_var) :
tag(tag_t::is_replace_th_var), r1(n), n1(nullptr), m_th_id(id), m_old_th_var(v) {}
update_record(new_lit) :
tag(tag_t::is_new_lit), r1(nullptr), n1(nullptr), r2_num_parents(0) {}
update_record(new_th_eq) :
tag(tag_t::is_new_th_eq), r1(nullptr), n1(nullptr), r2_num_parents(0) {}
update_record(unsigned qh, new_th_eq_qhead):
tag(tag_t::is_new_th_eq_qhead), r1(nullptr), n1(nullptr), qhead(qh) {}
update_record(unsigned qh, new_lits_qhead):
tag(tag_t::is_new_lits_qhead), r1(nullptr), n1(nullptr), qhead(qh) {}
update_record(bool inc, inconsistent) :
tag(tag_t::is_inconsistent), m_inconsistent(inc) {}
};
ast_manager& m; ast_manager& m;
trail_stack m_trail;
region m_region;
enode_vector m_worklist; enode_vector m_worklist;
etable m_table; etable m_table;
svector<add_eq_record> m_eqs; region m_region;
svector<scope> m_scopes; svector<update_record> m_updates;
unsigned_vector m_scopes;
enode_vector m_expr2enode; enode_vector m_expr2enode;
enode_vector m_nodes; enode_vector m_nodes;
expr_ref_vector m_exprs; expr_ref_vector m_exprs;
@ -101,9 +128,14 @@ namespace euf {
std::function<void(app*,app*)> m_used_cc; std::function<void(app*,app*)> m_used_cc;
void push_eq(enode* r1, enode* n1, unsigned r2_num_parents) { void push_eq(enode* r1, enode* n1, unsigned r2_num_parents) {
m_eqs.push_back(add_eq_record(r1, n1, r2_num_parents)); m_updates.push_back(update_record(r1, n1, r2_num_parents));
} }
void push_node(enode* n) { m_updates.push_back(update_record(n)); }
void add_th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r);
void add_literal(enode* n, bool is_eq);
void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents); void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents);
void undo_add_th_var(enode* n, theory_id id);
enode* mk_enode(expr* f, unsigned num_args, enode * const* args); enode* mk_enode(expr* f, unsigned num_args, enode * const* args);
void reinsert(enode* n); void reinsert(enode* n);
void force_push(); void force_push();
@ -173,10 +205,13 @@ namespace euf {
void add_th_var(enode* n, theory_var v, theory_id id); void add_th_var(enode* n, theory_var v, theory_id id);
void set_merge_enabled(enode* n, bool enable_merge);
void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; } void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; }
void set_used_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; } void set_used_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; }
void begin_explain();
void end_explain();
template <typename T> template <typename T>
void explain(ptr_vector<T>& justifications); void explain(ptr_vector<T>& justifications);
template <typename T> template <typename T>

View file

@ -36,21 +36,22 @@ namespace euf {
const theory_id null_theory_id = -1; const theory_id null_theory_id = -1;
class enode { class enode {
expr* m_owner; expr* m_owner{ nullptr };
bool m_mark1 { false }; bool m_mark1 { false };
bool m_mark2 { false }; bool m_mark2 { false };
bool m_commutative { false }; bool m_commutative { false };
bool m_update_children { false }; bool m_update_children { false };
bool m_interpreted { false }; bool m_interpreted { false };
bool m_merge_enabled { true };
unsigned m_class_size { 1 }; unsigned m_class_size { 1 };
unsigned m_table_id { UINT_MAX }; unsigned m_table_id { UINT_MAX };
enode_vector m_parents; enode_vector m_parents;
enode* m_next; enode* m_next{ nullptr };
enode* m_root; enode* m_root{ nullptr };
enode* m_target { nullptr }; enode* m_target { nullptr };
th_var_list m_th_vars; th_var_list m_th_vars;
justification m_justification; justification m_justification;
unsigned m_num_args; unsigned m_num_args { 0 };
enode* m_args[0]; enode* m_args[0];
friend class enode_args; friend class enode_args;
@ -73,6 +74,7 @@ namespace euf {
n->m_root = n; n->m_root = n;
n->m_commutative = num_args == 2 && is_app(f) && to_app(f)->get_decl()->is_commutative(); n->m_commutative = num_args == 2 && is_app(f) && to_app(f)->get_decl()->is_commutative();
n->m_num_args = num_args; n->m_num_args = num_args;
n->m_merge_enabled = true;
for (unsigned i = 0; i < num_args; ++i) { for (unsigned i = 0; i < num_args; ++i) {
SASSERT(to_app(f)->get_arg(i) == args[i]->get_owner()); SASSERT(to_app(f)->get_arg(i) == args[i]->get_owner());
n->m_args[i] = args[i]; n->m_args[i] = args[i];
@ -87,6 +89,7 @@ namespace euf {
void add_th_var(theory_var v, theory_id id, region & r) { m_th_vars.add_var(v, id, r); } void add_th_var(theory_var v, theory_id id, region & r) { m_th_vars.add_var(v, id, r); }
void replace_th_var(theory_var v, theory_id id) { m_th_vars.replace(v, id); } void replace_th_var(theory_var v, theory_id id) { m_th_vars.replace(v, id); }
void del_th_var(theory_id id) { m_th_vars.del_var(id); } void del_th_var(theory_id id) { m_th_vars.del_var(id); }
void set_merge_enabled(bool m) { m_merge_enabled = m; }
public: public:
~enode() { ~enode() {
@ -106,6 +109,7 @@ namespace euf {
bool interpreted() const { return m_interpreted; } bool interpreted() const { return m_interpreted; }
bool commutative() const { return m_commutative; } bool commutative() const { return m_commutative; }
void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; } void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; }
bool merge_enabled() { return m_merge_enabled; }
enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; } enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; }
unsigned hash() const { return m_owner->hash(); } unsigned hash() const { return m_owner->hash(); }
@ -143,6 +147,7 @@ namespace euf {
unsigned get_owner_id() const { return m_owner->get_id(); } unsigned get_owner_id() const { return m_owner->get_id(); }
unsigned get_root_id() const { return m_root->m_owner->get_id(); } unsigned get_root_id() const { return m_root->m_owner->get_id(); }
theory_var get_th_var(theory_id id) const { return m_th_vars.find(id); } theory_var get_th_var(theory_id id) const { return m_th_vars.find(id); }
bool is_attached_to(theory_id id) const { return get_th_var(id) != null_theory_var; }
bool has_th_vars() const { return !m_th_vars.empty(); } bool has_th_vars() const { return !m_th_vars.empty(); }
void inc_class_size(unsigned n) { m_class_size += n; } void inc_class_size(unsigned n) { m_class_size += n; }

View file

@ -45,14 +45,14 @@ class expr_pattern_match {
m_kind(k), m_offset(o), m_next(next), m_app(app), m_count(count) {} m_kind(k), m_offset(o), m_next(next), m_app(app), m_count(count) {}
instr_kind m_kind; instr_kind m_kind;
unsigned m_offset; unsigned m_offset{ 0 };
unsigned m_next; unsigned m_next{ 0 };
app* m_app; app* m_app{ nullptr };
expr* m_pat; expr* m_pat{ nullptr };
unsigned m_reg; unsigned m_reg{ 0 };
unsigned m_other_reg; unsigned m_other_reg{ 0 };
unsigned m_count; unsigned m_count{ 0 };
unsigned m_num_bound; unsigned m_num_bound{ 0 };
}; };
typedef obj_map<func_decl, unsigned> subst; typedef obj_map<func_decl, unsigned> subst;

View file

@ -30,7 +30,6 @@ Notes:
#include "ast/seq_decl_plugin.h" #include "ast/seq_decl_plugin.h"
#include "ast/pb_decl_plugin.h" #include "ast/pb_decl_plugin.h"
#include "ast/fpa_decl_plugin.h" #include "ast/fpa_decl_plugin.h"
#include "ast/csp_decl_plugin.h"
#include "ast/special_relations_decl_plugin.h" #include "ast/special_relations_decl_plugin.h"
#include "ast/ast_pp.h" #include "ast/ast_pp.h"
#include "ast/rewriter/var_subst.h" #include "ast/rewriter/var_subst.h"
@ -698,7 +697,6 @@ void cmd_context::init_manager_core(bool new_manager) {
register_plugin(symbol("pb"), alloc(pb_decl_plugin), logic_has_pb()); register_plugin(symbol("pb"), alloc(pb_decl_plugin), logic_has_pb());
register_plugin(symbol("fpa"), alloc(fpa_decl_plugin), logic_has_fpa()); register_plugin(symbol("fpa"), alloc(fpa_decl_plugin), logic_has_fpa());
register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin), !has_logic()); register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin), !has_logic());
register_plugin(symbol("csp"), alloc(csp_decl_plugin), smt_logics::logic_is_csp(m_logic));
register_plugin(symbol("specrels"), alloc(special_relations_decl_plugin), !has_logic()); register_plugin(symbol("specrels"), alloc(special_relations_decl_plugin), !has_logic());
} }
else { else {
@ -715,7 +713,6 @@ void cmd_context::init_manager_core(bool new_manager) {
load_plugin(symbol("seq"), logic_has_seq(), fids); load_plugin(symbol("seq"), logic_has_seq(), fids);
load_plugin(symbol("fpa"), logic_has_fpa(), fids); load_plugin(symbol("fpa"), logic_has_fpa(), fids);
load_plugin(symbol("pb"), logic_has_pb(), fids); load_plugin(symbol("pb"), logic_has_pb(), fids);
load_plugin(symbol("csp"), smt_logics::logic_is_csp(m_logic), fids);
for (family_id fid : fids) { for (family_id fid : fids) {
decl_plugin * p = m_manager->get_plugin(fid); decl_plugin * p = m_manager->get_plugin(fid);

View file

@ -20,43 +20,21 @@ Revision History:
#undef max #undef max
#undef min #undef min
#include "sat/sat_solver.h" #include "sat/sat_solver.h"
#include "sat/sat_drat.h"
namespace {
struct lex_error {};
class stream_buffer {
std::istream & m_stream;
int m_val;
unsigned m_line;
public:
stream_buffer(std::istream & s):
m_stream(s),
m_line(0) {
m_val = m_stream.get();
}
int operator *() const {
return m_val;
}
void operator ++() {
m_val = m_stream.get();
if (m_val == '\n') ++m_line;
}
unsigned line() const { return m_line; }
};
template<typename Buffer> template<typename Buffer>
void skip_whitespace(Buffer & in) { static bool is_whitespace(Buffer & in) {
while ((*in >= 9 && *in <= 13) || *in == 32) { return (*in >= 9 && *in <= 13) || *in == 32;
}
template<typename Buffer>
static void skip_whitespace(Buffer & in) {
while (is_whitespace(in))
++in; ++in;
} }
}
template<typename Buffer> template<typename Buffer>
void skip_line(Buffer & in) { static void skip_line(Buffer & in) {
while(true) { while(true) {
if (*in == EOF) { if (*in == EOF) {
return; return;
@ -70,7 +48,7 @@ void skip_line(Buffer & in) {
} }
template<typename Buffer> template<typename Buffer>
int parse_int(Buffer & in, std::ostream& err) { static int parse_int(Buffer & in, std::ostream& err) {
int val = 0; int val = 0;
bool neg = false; bool neg = false;
skip_whitespace(in); skip_whitespace(in);
@ -84,8 +62,11 @@ int parse_int(Buffer & in, std::ostream& err) {
} }
if (*in < '0' || *in > '9') { if (*in < '0' || *in > '9') {
if (20 <= *in && *in < 128)
err << "(error, \"unexpected char: " << ((char)*in) << " line: " << in.line() << "\")\n";
else
err << "(error, \"unexpected char: " << *in << " line: " << in.line() << "\")\n"; err << "(error, \"unexpected char: " << *in << " line: " << in.line() << "\")\n";
throw lex_error(); throw dimacs::lex_error();
} }
while (*in >= '0' && *in <= '9') { while (*in >= '0' && *in <= '9') {
@ -97,7 +78,7 @@ int parse_int(Buffer & in, std::ostream& err) {
} }
template<typename Buffer> template<typename Buffer>
void read_clause(Buffer & in, std::ostream& err, sat::solver & solver, sat::literal_vector & lits) { static void read_clause(Buffer & in, std::ostream& err, sat::solver & solver, sat::literal_vector & lits) {
int parsed_lit; int parsed_lit;
int var; int var;
@ -116,7 +97,24 @@ void read_clause(Buffer & in, std::ostream& err, sat::solver & solver, sat::lite
} }
template<typename Buffer> template<typename Buffer>
bool parse_dimacs_core(Buffer & in, std::ostream& err, sat::solver & solver) { static void read_clause(Buffer & in, std::ostream& err, sat::literal_vector & lits) {
int parsed_lit;
int var;
lits.reset();
while (true) {
parsed_lit = parse_int(in, err);
if (parsed_lit == 0)
break;
var = abs(parsed_lit);
SASSERT(var > 0);
lits.push_back(sat::literal(var, parsed_lit < 0));
}
}
template<typename Buffer>
static bool parse_dimacs_core(Buffer & in, std::ostream& err, sat::solver & solver) {
sat::literal_vector lits; sat::literal_vector lits;
try { try {
while (true) { while (true) {
@ -133,15 +131,158 @@ bool parse_dimacs_core(Buffer & in, std::ostream& err, sat::solver & solver) {
} }
} }
} }
catch (lex_error) { catch (dimacs::lex_error) {
return false; return false;
} }
return true; return true;
} }
}
bool parse_dimacs(std::istream & in, std::ostream& err, sat::solver & solver) { bool parse_dimacs(std::istream & in, std::ostream& err, sat::solver & solver) {
stream_buffer _in(in); dimacs::stream_buffer _in(in);
return parse_dimacs_core(_in, err, solver); return parse_dimacs_core(_in, err, solver);
} }
namespace dimacs {
std::ostream& operator<<(std::ostream& out, drat_record const& r) {
switch (r.m_tag) {
case drat_record::tag_t::is_clause:
return out << r.m_status << " " << r.m_lits << "\n";
case drat_record::tag_t::is_node:
return out << "e " << r.m_node_id << " " << r.m_name << " " << r.m_args << "\n";
case drat_record::is_bool_def:
return out << "b " << r.m_node_id << " " << r.m_args << "\n";
}
return out;
}
char const* drat_parser::parse_identifier() {
m_buffer.reset();
while (!is_whitespace(in)) {
m_buffer.push_back(*in);
++in;
}
m_buffer.push_back(0);
return m_buffer.c_ptr();
}
char const* drat_parser::parse_sexpr() {
m_buffer.reset();
unsigned lp = 0;
while (!is_whitespace(in) || lp > 0) {
m_buffer.push_back(*in);
if (*in == '(')
++lp;
else if (*in == ')') {
if (lp == 0) {
throw lex_error();
}
else --lp;
}
++in;
}
m_buffer.push_back(0);
return m_buffer.c_ptr();
}
int drat_parser::read_theory_id() {
skip_whitespace(in);
if ('a' <= *in && *in <= 'z') {
if (!m_read_theory_id)
throw lex_error();
return m_read_theory_id(parse_identifier());
}
else {
return -1;
}
}
bool drat_parser::next() {
int n, b, e, theory_id;
try {
loop:
skip_whitespace(in);
switch (*in) {
case EOF:
return false;
case 'c':
case 'p':
skip_line(in);
goto loop;
case 'i':
++in;
skip_whitespace(in);
read_clause(in, err, m_record.m_lits);
m_record.m_tag = drat_record::tag_t::is_clause;
m_record.m_status = sat::status::input();
break;
case 'a':
++in;
skip_whitespace(in);
theory_id = read_theory_id();
skip_whitespace(in);
read_clause(in, err, m_record.m_lits);
m_record.m_tag = drat_record::tag_t::is_clause;
m_record.m_status = sat::status::th(false, theory_id);
break;
case 'e':
++in;
skip_whitespace(in);
n = parse_int(in, err);
skip_whitespace(in);
m_record.m_name = parse_sexpr();
m_record.m_tag = drat_record::tag_t::is_node;
m_record.m_node_id = n;
m_record.m_args.reset();
while (true) {
n = parse_int(in, err);
if (n == 0)
break;
if (n < 0)
throw lex_error();
m_record.m_args.push_back(n);
}
break;
case 'b':
++in;
skip_whitespace(in);
b = parse_int(in, err);
n = parse_int(in, err);
e = parse_int(in, err);
if (e != 0)
throw lex_error();
m_record.m_tag = drat_record::tag_t::is_bool_def;
m_record.m_node_id = b;
m_record.m_args.reset();
m_record.m_args.push_back(n);
break;
case 'd':
++in;
skip_whitespace(in);
read_clause(in, err, m_record.m_lits);
m_record.m_tag = drat_record::tag_t::is_clause;
m_record.m_status = sat::status::deleted();
break;
case 'r':
++in;
skip_whitespace(in);
theory_id = read_theory_id();
read_clause(in, err, m_record.m_lits);
m_record.m_tag = drat_record::tag_t::is_clause;
m_record.m_status = sat::status::th(true, theory_id);
break;
default:
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();
break;
}
return true;
}
catch (lex_error) {
return false;
}
}
}

View file

@ -15,6 +15,9 @@ Author:
Revision History: Revision History:
Nikolaj Bjorner (nbjorner) 2020-09-07
Add parser to consume extended DRAT format.
--*/ --*/
#pragma once #pragma once
@ -22,4 +25,87 @@ Revision History:
bool parse_dimacs(std::istream & s, std::ostream& err, sat::solver & solver); bool parse_dimacs(std::istream & s, std::ostream& err, sat::solver & solver);
namespace dimacs {
struct lex_error {};
class stream_buffer {
std::istream & m_stream;
int m_val;
unsigned m_line;
public:
stream_buffer(std::istream & s):
m_stream(s),
m_line(0) {
m_val = m_stream.get();
}
int operator *() const {
return m_val;
}
void operator ++() {
m_val = m_stream.get();
if (m_val == '\n') ++m_line;
}
unsigned line() const { return m_line; }
};
struct drat_record {
enum tag_t { is_clause, is_node, is_bool_def };
tag_t m_tag;
// a clause populates m_lits and m_status
// 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;
unsigned m_node_id;
std::string m_name;
unsigned_vector m_args;
drat_record():
m_tag(is_clause),
m_status(sat::status::redundant())
{}
};
std::ostream& operator<<(std::ostream& out, drat_record const& r);
class drat_parser {
dimacs::stream_buffer in;
std::ostream& err;
drat_record m_record;
std::function<int(char const*)> m_read_theory_id;
svector<char> m_buffer;
char const* parse_sexpr();
char const* parse_identifier();
int read_theory_id();
bool next();
public:
drat_parser(std::istream & _in, std::ostream& err):
in(_in), err(err)
{}
class iterator {
drat_parser& p;
bool m_eof;
public:
iterator(drat_parser& p, bool is_eof):p(p), m_eof(is_eof) { if (!m_eof) m_eof = !p.next(); }
drat_record const& operator*() { return p.m_record; }
iterator& operator++() { if (!p.next()) m_eof = true; return *this;}
bool operator==(iterator const& other) const { return m_eof == other.m_eof; }
bool operator!=(iterator const& other) const { return m_eof != other.m_eof; }
};
iterator begin() { return iterator(*this, false); };
iterator end() { return iterator(*this, true); }
void set_read_theory(std::function<int(char const*)>& r) { m_read_theory_id = r; }
};
};

View file

@ -69,11 +69,11 @@ namespace sat {
// encodes one of var, and, !and, xor, !xor, ite, !ite. // encodes one of var, and, !and, xor, !xor, ite, !ite.
class node { class node {
bool m_sign; bool m_sign{ false };
bool_op m_op; bool_op m_op{ no_op };
uint64_t m_lut; uint64_t m_lut{ 0 };
unsigned m_size; unsigned m_size{ 0 };
unsigned m_offset; unsigned m_offset{ 0 };
public: public:
node(): node():
m_sign(false), m_op(no_op), m_size(UINT_MAX), m_offset(UINT_MAX) {} m_sign(false), m_op(no_op), m_size(UINT_MAX), m_offset(UINT_MAX) {}

View file

@ -38,13 +38,13 @@ namespace sat {
solver& m_solver; solver& m_solver;
scoped_ptr<solver> s; scoped_ptr<solver> s;
unsigned m_bin_clauses; unsigned m_bin_clauses{ 0 };
unsigned m_stopped_at; unsigned m_stopped_at{ 0 };
vector<clause_vector> m_use_list; vector<clause_vector> m_use_list;
unsigned m_limit1, m_limit2; unsigned m_limit1{ 0 }, m_limit2{ 0 };
bool_vector m_mark, m_mark2; bool_vector m_mark, m_mark2;
literal_vector m_must_candidates, m_may_candidates; literal_vector m_must_candidates, m_may_candidates;
unsigned m_state; unsigned m_state{ 0 };
void init_g() { m_state = 0x7; } void init_g() { m_state = 0x7; }
void g_add_binary(literal l1, literal l2, bool flip2); void g_add_binary(literal l1, literal l2, bool flip2);

View file

@ -29,14 +29,13 @@ namespace sat {
m_out(nullptr), m_out(nullptr),
m_bout(nullptr), m_bout(nullptr),
m_inconsistent(false), m_inconsistent(false),
m_num_add(0),
m_num_del(0),
m_check_unsat(false), m_check_unsat(false),
m_check_sat(false), m_check_sat(false),
m_check(false), m_check(false),
m_activity(false) m_activity(false)
{ {
if (s.get_config().m_drat && s.get_config().m_drat_file != symbol()) { if (s.get_config().m_drat && s.get_config().m_drat_file.is_non_empty_string()) {
std::cout << "DRAT " << s.get_config().m_drat_file << "\n";
auto mode = s.get_config().m_drat_binary ? (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc) : std::ios_base::out; auto mode = s.get_config().m_drat_binary ? (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc) : std::ios_base::out;
m_out = alloc(std::ofstream, s.get_config().m_drat_file.str(), mode); m_out = alloc(std::ofstream, s.get_config().m_drat_file.str(), mode);
if (s.get_config().m_drat_binary) { if (s.get_config().m_drat_binary) {
@ -75,6 +74,8 @@ namespace sat {
out << "d"; out << "d";
else if (st.is_asserted()) else if (st.is_asserted())
out << "a"; out << "a";
else if (st.is_input())
out << "i";
if (!st.is_sat()) if (!st.is_sat())
out << " " << m_theory[st.get_th()]; out << " " << m_theory[st.get_th()];
@ -84,7 +85,7 @@ namespace sat {
void drat::dump(unsigned n, literal const* c, status st) { void drat::dump(unsigned n, literal const* c, status st) {
if (st.is_asserted() && !s.m_ext) if (st.is_asserted() && !s.m_ext)
return; return;
if (m_activity && ((m_num_add % 1000) == 0)) if (m_activity && ((m_stats.m_num_add % 1000) == 0))
dump_activity(); dump_activity();
char buffer[10000]; char buffer[10000];
@ -100,6 +101,14 @@ namespace sat {
buffer[len++] = 'd'; buffer[len++] = 'd';
buffer[len++] = ' '; buffer[len++] = ' ';
} }
else if (st.is_input()) {
buffer[len++] = 'i';
buffer[len++] = ' ';
}
else if (st.is_redundant() && !st.is_sat()) {
buffer[len++] = 'r';
buffer[len++] = ' ';
}
if (!st.is_sat()) { if (!st.is_sat()) {
for (char ch : m_theory[st.get_th()]) for (char ch : m_theory[st.get_th()])
@ -164,8 +173,7 @@ namespace sat {
m_bout->write(buffer, len); m_bout->write(buffer, len);
len = 0; len = 0;
} }
} } while (v);
while (v);
} }
buffer[len++] = 0; buffer[len++] = 0;
m_bout->write(buffer, len); m_bout->write(buffer, len);
@ -198,7 +206,7 @@ namespace sat {
declare(l); declare(l);
IF_VERBOSE(20, trace(verbose_stream(), 1, &l, st);); IF_VERBOSE(20, trace(verbose_stream(), 1, &l, st););
if (st.is_redundant()) { if (st.is_redundant() && st.is_sat()) {
verify(1, &l); verify(1, &l);
} }
if (st.is_deleted()) { if (st.is_deleted()) {
@ -223,7 +231,7 @@ namespace sat {
// don't record binary as deleted. // don't record binary as deleted.
} }
else { else {
if (st.is_redundant()) { if (st.is_redundant() && st.is_sat()) {
verify(2, lits); verify(2, lits);
} }
clause* c = m_alloc.mk_clause(2, lits, st.is_redundant()); clause* c = m_alloc.mk_clause(2, lits, st.is_redundant());
@ -252,9 +260,9 @@ namespace sat {
(*m_out) << "b " << v << " " << n << " 0\n"; (*m_out) << "b " << v << " " << n << " 0\n";
} }
void drat::def_begin(unsigned n, symbol const& name) { void drat::def_begin(unsigned n, std::string const& name) {
if (m_out) if (m_out)
(*m_out) << "n " << n << " " << name; (*m_out) << "e " << n << " " << name;
} }
void drat::def_add_arg(unsigned arg) { void drat::def_add_arg(unsigned arg) {
@ -300,7 +308,7 @@ namespace sat {
unsigned n = c.size(); unsigned n = c.size();
IF_VERBOSE(20, trace(verbose_stream(), n, c.begin(), st);); IF_VERBOSE(20, trace(verbose_stream(), n, c.begin(), st););
if (st.is_redundant()) { if (st.is_redundant() && st.is_sat()) {
verify(c); verify(c);
} }
@ -435,17 +443,16 @@ namespace sat {
} }
m_units.shrink(num_units); m_units.shrink(num_units);
bool ok = m_inconsistent; bool ok = m_inconsistent;
// IF_VERBOSE(9, verbose_stream() << "is-drup " << m_inconsistent << "\n");
m_inconsistent = false; m_inconsistent = false;
return ok; return ok;
} }
bool drat::is_drat(unsigned n, literal const* c) { bool drat::is_drat(unsigned n, literal const* c) {
if (m_inconsistent || n == 0) return true; if (m_inconsistent || n == 0)
for (unsigned i = 0; i < n; ++i) { return true;
if (is_drat(n, c, i)) return true; for (unsigned i = 0; i < n; ++i)
} if (is_drat(n, c, i))
return true;
return false; return false;
} }
@ -499,7 +506,15 @@ namespace sat {
for (unsigned i = 0; i < n; ++i) { for (unsigned i = 0; i < n; ++i) {
declare(c[i]); declare(c[i]);
} }
if (!is_drup(n, c) && !is_drat(n, c)) { if (is_drup(n, c)) {
++m_stats.m_num_drup;
return;
}
if (is_drat(n, c)) {
++m_stats.m_num_drat;
return;
}
literal_vector lits(n, c); literal_vector lits(n, c);
IF_VERBOSE(0, verbose_stream() << "Verification of " << lits << " failed\n"); IF_VERBOSE(0, verbose_stream() << "Verification of " << lits << " failed\n");
// s.display(std::cout); // s.display(std::cout);
@ -516,7 +531,6 @@ namespace sat {
s.display(tout);); s.display(tout););
UNREACHABLE(); UNREACHABLE();
} }
}
bool drat::contains(literal c, justification const& j) { bool drat::contains(literal c, justification const& j) {
if (!m_check_sat) { if (!m_check_sat) {
@ -705,7 +719,7 @@ namespace sat {
} }
void drat::add() { void drat::add() {
++m_num_add; ++m_stats.m_num_add;
if (m_out) (*m_out) << "0\n"; if (m_out) (*m_out) << "0\n";
if (m_bout) bdump(0, nullptr, status::redundant()); if (m_bout) bdump(0, nullptr, status::redundant());
if (m_check_unsat) { if (m_check_unsat) {
@ -713,7 +727,7 @@ namespace sat {
} }
} }
void drat::add(literal l, bool learned) { void drat::add(literal l, bool learned) {
++m_num_add; ++m_stats.m_num_add;
status st = get_status(learned); status st = get_status(learned);
if (m_out) dump(1, &l, st); if (m_out) dump(1, &l, st);
if (m_bout) bdump(1, &l, st); if (m_bout) bdump(1, &l, st);
@ -721,9 +735,9 @@ namespace sat {
} }
void drat::add(literal l1, literal l2, status st) { void drat::add(literal l1, literal l2, status st) {
if (st.is_deleted()) if (st.is_deleted())
++m_num_del; ++m_stats.m_num_del;
else else
++m_num_add; ++m_stats.m_num_add;
literal ls[2] = { l1, l2 }; literal ls[2] = { l1, l2 };
if (m_out) dump(2, ls, st); if (m_out) dump(2, ls, st);
if (m_bout) bdump(2, ls, st); if (m_bout) bdump(2, ls, st);
@ -731,9 +745,9 @@ namespace sat {
} }
void drat::add(clause& c, status st) { void drat::add(clause& c, status st) {
if (st.is_deleted()) if (st.is_deleted())
++m_num_del; ++m_stats.m_num_del;
else else
++m_num_add; ++m_stats.m_num_add;
if (m_out) dump(c.size(), c.begin(), st); if (m_out) dump(c.size(), c.begin(), st);
if (m_bout) bdump(c.size(), c.begin(), st); if (m_bout) bdump(c.size(), c.begin(), st);
if (m_check) { if (m_check) {
@ -743,9 +757,9 @@ namespace sat {
} }
void drat::add(literal_vector const& lits, status st) { void drat::add(literal_vector const& lits, status st) {
if (st.is_deleted()) if (st.is_deleted())
++m_num_del; ++m_stats.m_num_del;
else else
++m_num_add; ++m_stats.m_num_add;
if (m_check) { if (m_check) {
switch (lits.size()) { switch (lits.size()) {
case 0: add(); break; case 0: add(); break;
@ -761,7 +775,7 @@ namespace sat {
dump(lits.size(), lits.c_ptr(), st); dump(lits.size(), lits.c_ptr(), st);
} }
void drat::add(literal_vector const& c) { void drat::add(literal_vector const& c) {
++m_num_add; ++m_stats.m_num_add;
if (m_out) dump(c.size(), c.begin(), status::redundant()); if (m_out) dump(c.size(), c.begin(), status::redundant());
if (m_bout) bdump(c.size(), c.begin(), status::redundant()); if (m_bout) bdump(c.size(), c.begin(), status::redundant());
if (m_check) { if (m_check) {
@ -780,14 +794,14 @@ namespace sat {
} }
void drat::del(literal l) { void drat::del(literal l) {
++m_num_del; ++m_stats.m_num_del;
if (m_out) dump(1, &l, status::deleted()); if (m_out) dump(1, &l, status::deleted());
if (m_bout) bdump(1, &l, status::deleted()); if (m_bout) bdump(1, &l, status::deleted());
if (m_check_unsat) append(l, status::deleted()); if (m_check_unsat) append(l, status::deleted());
} }
void drat::del(literal l1, literal l2) { void drat::del(literal l1, literal l2) {
++m_num_del; ++m_stats.m_num_del;
literal ls[2] = { l1, l2 }; literal ls[2] = { l1, l2 };
if (m_out) dump(2, ls, status::deleted()); if (m_out) dump(2, ls, status::deleted());
if (m_bout) bdump(2, ls, status::deleted()); if (m_bout) bdump(2, ls, status::deleted());
@ -806,7 +820,7 @@ namespace sat {
m_seen[lit.index()] = false; m_seen[lit.index()] = false;
} }
#endif #endif
++m_num_del; ++m_stats.m_num_del;
if (m_out) dump(c.size(), c.begin(), status::deleted()); if (m_out) dump(c.size(), c.begin(), status::deleted());
if (m_bout) bdump(c.size(), c.begin(), status::deleted()); if (m_bout) bdump(c.size(), c.begin(), status::deleted());
if (m_check) { if (m_check) {
@ -816,7 +830,7 @@ namespace sat {
} }
void drat::del(literal_vector const& c) { void drat::del(literal_vector const& c) {
++m_num_del; ++m_stats.m_num_del;
if (m_out) dump(c.size(), c.begin(), status::deleted()); if (m_out) dump(c.size(), c.begin(), status::deleted());
if (m_bout) bdump(c.size(), c.begin(), status::deleted()); if (m_bout) bdump(c.size(), c.begin(), status::deleted());
if (m_check) { if (m_check) {
@ -828,4 +842,25 @@ namespace sat {
void drat::check_model(model const& m) { void drat::check_model(model const& m) {
} }
std::ostream& operator<<(std::ostream& out, status const& st) {
if (st.is_deleted())
out << "d";
else if (st.is_input())
out << "i";
else if (st.is_asserted())
out << "a";
else if (st.is_redundant() && !st.is_sat())
out << "r";
if (!st.is_sat())
out << " th" << st.m_orig;
return out;
}
void drat::collect_statistics(statistics& st) const {
st.update("num-drup", m_stats.m_num_drup);
st.update("num-drat", m_stats.m_num_drat);
st.update("num-add", m_stats.m_num_add);
st.update("num-del", m_stats.m_num_del);
}
} }

View file

@ -19,18 +19,24 @@ Notes:
For SMT extensions are as follows: For SMT extensions are as follows:
Assertion (trusted modulo internalizer): Input assertion:
i <literal>* 0
Assertion (true modulo a theory):
a [<theory-id>] <literal>* 0 a [<theory-id>] <literal>* 0
The optional theory id indicates the assertion is irredundant The if no theory id is given, the assertion is a tautology
modulo Tseitin converison. Theory ids track whether the
tautology is modulo a theory.
Assertions are irredundant.
Bridge from ast-node to boolean variable: Bridge from ast-node to boolean variable:
b <bool-var-id> <ast-node-id> 0 b <bool-var-id> <ast-node-id> 0
Definition of an ast node: Definition of an expression (ast-node):
n <ast-node-id> <name> <ast-node-id>* 0 e <ast-node-id> <name> <ast-node-id>* 0
Theory lemma Redundant clause (theory lemma if theory id is given)
<theory-id> <literal>* 0 [r [<theory-id>]] <literal>* 0
Available theories are: Available theories are:
- euf The theory lemma should be a consequence of congruence closure. - euf The theory lemma should be a consequence of congruence closure.
@ -45,8 +51,16 @@ Notes:
#include "sat_types.h" #include "sat_types.h"
namespace sat { namespace sat {
class justification;
class clause;
class drat { class drat {
private: struct stats {
unsigned m_num_drup { 0 };
unsigned m_num_drat { 0 };
unsigned m_num_add { 0 };
unsigned m_num_del { 0 };
};
struct watched_clause { struct watched_clause {
clause* m_clause; clause* m_clause;
literal m_l1, m_l2; literal m_l1, m_l2;
@ -64,10 +78,10 @@ namespace sat {
literal_vector m_units; literal_vector m_units;
vector<watch> m_watches; vector<watch> m_watches;
svector<lbool> m_assignment; svector<lbool> m_assignment;
svector<std::string> m_theory; vector<std::string> m_theory;
bool m_inconsistent; bool m_inconsistent;
unsigned m_num_add, m_num_del;
bool m_check_unsat, m_check_sat, m_check, m_activity; bool m_check_unsat, m_check_sat, m_check, m_activity;
stats m_stats;
void dump_activity(); void dump_activity();
void dump(unsigned n, literal const* c, status st); void dump(unsigned n, literal const* c, status st);
@ -96,6 +110,7 @@ namespace sat {
bool match(unsigned n, literal const* lits, clause const& c) const; bool match(unsigned n, literal const* lits, clause const& c) const;
public: public:
drat(solver& s); drat(solver& s);
~drat(); ~drat();
@ -114,7 +129,7 @@ namespace sat {
void bool_def(bool_var v, unsigned n); void bool_def(bool_var v, unsigned n);
// declare AST node n with 'name' and arguments arg // declare AST node n with 'name' and arguments arg
void def_begin(unsigned n, symbol const& name); void def_begin(unsigned n, std::string const& name);
void def_add_arg(unsigned arg); void def_add_arg(unsigned arg);
void def_end(); void def_end();
@ -139,7 +154,11 @@ namespace sat {
bool contains(literal c, justification const& j); bool contains(literal c, justification const& j);
void check_model(model const& m); void check_model(model const& m);
void collect_statistics(statistics& st) const;
}; };
std::ostream& operator<<(std::ostream& out, status const& st);
}; };

View file

@ -236,7 +236,6 @@ namespace sat {
if (m_solver.is_assumption(v) || (m_solver.is_external(v) && (m_solver.is_incremental() || !root_ok))) { if (m_solver.is_assumption(v) || (m_solver.is_external(v) && (m_solver.is_incremental() || !root_ok))) {
// cannot really eliminate v, since we have to notify extension of future assignments // cannot really eliminate v, since we have to notify extension of future assignments
if (m_solver.m_config.m_drat && m_solver.m_config.m_drat_file.is_null()) { if (m_solver.m_config.m_drat && m_solver.m_config.m_drat_file.is_null()) {
std::cout << "DRAT\n";
m_solver.m_drat.add(~l, r, sat::status::redundant()); m_solver.m_drat.add(~l, r, sat::status::redundant());
m_solver.m_drat.add(l, ~r, sat::status::redundant()); m_solver.m_drat.add(l, ~r, sat::status::redundant());
} }

View file

@ -61,9 +61,10 @@ namespace sat {
virtual void set_lookahead(lookahead* s) = 0; virtual void set_lookahead(lookahead* s) = 0;
virtual void init_search() {} virtual void init_search() {}
virtual bool propagate(literal l, ext_constraint_idx idx) = 0; virtual bool propagate(literal l, ext_constraint_idx idx) = 0;
virtual bool unit_propagate() = 0;
virtual bool is_external(bool_var v) = 0; virtual bool is_external(bool_var v) = 0;
virtual double get_reward(literal l, ext_constraint_idx idx, literal_occs_fun& occs) const = 0; virtual double get_reward(literal l, ext_constraint_idx idx, literal_occs_fun& occs) const = 0;
virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) = 0; virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r, bool probing) = 0;
virtual bool is_extended_binary(ext_justification_idx idx, literal_vector & r) = 0; virtual bool is_extended_binary(ext_justification_idx idx, literal_vector & r) = 0;
virtual void asserted(literal l) = 0; virtual void asserted(literal l) = 0;
virtual check_result check() = 0; virtual check_result check() = 0;

View file

@ -1900,7 +1900,6 @@ namespace sat {
} }
bool simplifier::try_eliminate(bool_var v) { bool simplifier::try_eliminate(bool_var v) {
TRACE("sat_simplifier", tout << "processing: " << v << "\n";);
if (value(v) != l_undef) if (value(v) != l_undef)
return false; return false;
@ -1963,7 +1962,7 @@ namespace sat {
} }
} }
} }
TRACE("sat_simplifier", tout << "found var to eliminate, before: " << before_clauses << " after: " << after_clauses << "\n";); TRACE("sat_simplifier", tout << "eliminate " << v << ", before: " << before_clauses << " after: " << after_clauses << "\n";);
m_elim_counter -= num_pos * num_neg + before_lits; m_elim_counter -= num_pos * num_neg + before_lits;
m_elim_counter -= num_pos * num_neg + before_lits; m_elim_counter -= num_pos * num_neg + before_lits;

View file

@ -86,7 +86,7 @@ namespace sat {
m_cuber = nullptr; m_cuber = nullptr;
m_local_search = nullptr; m_local_search = nullptr;
m_mc.set_solver(this); m_mc.set_solver(this);
//mk_var(false, false); mk_var(false, false);
} }
solver::~solver() { solver::~solver() {
@ -138,7 +138,7 @@ namespace sat {
m_qhead = 0; m_qhead = 0;
m_trail.reset(); m_trail.reset();
m_scopes.reset(); m_scopes.reset();
//mk_var(false, false); mk_var(false, false);
if (src.inconsistent()) { if (src.inconsistent()) {
set_conflict(); set_conflict();
@ -353,7 +353,7 @@ namespace sat {
clause * solver::mk_clause_core(unsigned num_lits, literal * lits, sat::status st) { clause * solver::mk_clause_core(unsigned num_lits, literal * lits, sat::status st) {
bool redundant = st.is_redundant(); bool redundant = st.is_redundant();
TRACE("sat", tout << "mk_clause: " << mk_lits_pp(num_lits, lits) << (redundant?" learned":" aux") << "\n";); TRACE("sat", tout << "mk_clause: " << mk_lits_pp(num_lits, lits) << (redundant?" learned":" aux") << "\n";);
if (!redundant) { if (!redundant || !st.is_sat()) {
unsigned old_sz = num_lits; unsigned old_sz = num_lits;
bool keep = simplify_clause(num_lits, lits); bool keep = simplify_clause(num_lits, lits);
TRACE("sat_mk_clause", tout << "mk_clause (after simp), keep: " << keep << "\n" << mk_lits_pp(num_lits, lits) << "\n";); TRACE("sat_mk_clause", tout << "mk_clause (after simp), keep: " << keep << "\n" << mk_lits_pp(num_lits, lits) << "\n";);
@ -1103,6 +1103,11 @@ namespace sat {
} }
} }
wlist.set_end(it2); wlist.set_end(it2);
if (m_ext) {
m_ext->unit_propagate();
if (inconsistent())
return false;
}
} }
SASSERT(m_qhead == m_trail.size()); SASSERT(m_qhead == m_trail.size());
SASSERT(!m_inconsistent); SASSERT(!m_inconsistent);
@ -1205,9 +1210,9 @@ namespace sat {
} }
try { try {
init_search(); init_search();
if (inconsistent()) return l_false; if (check_inconsistent()) return l_false;
propagate(false); propagate(false);
if (inconsistent()) return l_false; if (check_inconsistent()) return l_false;
init_assumptions(num_lits, lits); init_assumptions(num_lits, lits);
propagate(false); propagate(false);
if (check_inconsistent()) return l_false; if (check_inconsistent()) return l_false;
@ -1644,7 +1649,9 @@ namespace sat {
bool solver::check_inconsistent() { bool solver::check_inconsistent() {
if (inconsistent()) { if (inconsistent()) {
if (tracking_assumptions()) if (tracking_assumptions() && at_search_lvl())
resolve_conflict();
else if (m_config.m_drat && at_base_lvl())
resolve_conflict(); resolve_conflict();
return true; return true;
} }
@ -2576,13 +2583,6 @@ namespace sat {
if (m_step_size > m_config.m_step_size_min) { if (m_step_size > m_config.m_step_size_min) {
m_step_size -= m_config.m_step_size_dec; m_step_size -= m_config.m_step_size_dec;
} }
struct reset_cache {
solver& s;
bool active;
reset_cache(solver& s) :s(s), active(true) {}
~reset_cache() { if (active) s.m_cached_antecedent_js = 0; }
};
reset_cache _reset(*this);
bool unique_max; bool unique_max;
m_conflict_lvl = get_max_lvl(m_not_l, m_conflict, unique_max); m_conflict_lvl = get_max_lvl(m_not_l, m_conflict, unique_max);
justification js = m_conflict; justification js = m_conflict;
@ -2609,7 +2609,6 @@ namespace sat {
pop_reinit(m_scope_lvl - m_conflict_lvl + 1); pop_reinit(m_scope_lvl - m_conflict_lvl + 1);
m_force_conflict_analysis = true; m_force_conflict_analysis = true;
++m_stats.m_backtracks; ++m_stats.m_backtracks;
_reset.active = false;
return l_undef; return l_undef;
} }
m_force_conflict_analysis = false; m_force_conflict_analysis = false;
@ -2685,7 +2684,7 @@ namespace sat {
break; break;
} }
case justification::EXT_JUSTIFICATION: { case justification::EXT_JUSTIFICATION: {
fill_ext_antecedents(consequent, js); fill_ext_antecedents(consequent, js, false);
for (literal l : m_ext_antecedents) for (literal l : m_ext_antecedents)
process_antecedent(l, num_marks); process_antecedent(l, num_marks);
break; break;
@ -2786,6 +2785,7 @@ namespace sat {
if (m_par && lemma) { if (m_par && lemma) {
m_par->share_clause(*this, *lemma); m_par->share_clause(*this, *lemma);
} }
m_lemma.reset();
TRACE("sat_conflict_detail", tout << "consistent " << (!m_inconsistent) << " scopes: " << scope_lvl() << " backtrack: " << backtrack_lvl << " backjump: " << backjump_lvl << "\n";); TRACE("sat_conflict_detail", tout << "consistent " << (!m_inconsistent) << " scopes: " << scope_lvl() << " backtrack: " << backtrack_lvl << " backjump: " << backjump_lvl << "\n";);
decay_activity(); decay_activity();
updt_phase_counters(); updt_phase_counters();
@ -2847,7 +2847,7 @@ namespace sat {
break; break;
} }
case justification::EXT_JUSTIFICATION: { case justification::EXT_JUSTIFICATION: {
fill_ext_antecedents(consequent, js); fill_ext_antecedents(consequent, js, true);
for (literal l : m_ext_antecedents) { for (literal l : m_ext_antecedents) {
process_antecedent_for_unsat_core(l); process_antecedent_for_unsat_core(l);
} }
@ -2975,7 +2975,7 @@ namespace sat {
case justification::EXT_JUSTIFICATION: case justification::EXT_JUSTIFICATION:
if (not_l != null_literal) if (not_l != null_literal)
not_l.neg(); not_l.neg();
fill_ext_antecedents(not_l, js); fill_ext_antecedents(not_l, js, true);
for (literal l : m_ext_antecedents) for (literal l : m_ext_antecedents)
level = update_max_level(l, level, unique_max); level = update_max_level(l, level, unique_max);
return level; return level;
@ -3031,16 +3031,12 @@ namespace sat {
/** /**
\brief js is an external justification. Collect its antecedents and store at m_ext_antecedents. \brief js is an external justification. Collect its antecedents and store at m_ext_antecedents.
*/ */
void solver::fill_ext_antecedents(literal consequent, justification js) { void solver::fill_ext_antecedents(literal consequent, justification js, bool probing) {
SASSERT(js.is_ext_justification()); SASSERT(js.is_ext_justification());
SASSERT(m_ext); SASSERT(m_ext);
auto idx = js.get_ext_justification_idx(); auto idx = js.get_ext_justification_idx();
if (consequent == m_cached_antecedent_consequent && idx == m_cached_antecedent_js)
return;
m_ext_antecedents.reset(); m_ext_antecedents.reset();
m_ext->get_antecedents(consequent, idx, m_ext_antecedents); m_ext->get_antecedents(consequent, idx, m_ext_antecedents, probing);
m_cached_antecedent_consequent = consequent;
m_cached_antecedent_js = idx;
} }
bool solver::is_two_phase() const { bool solver::is_two_phase() const {
@ -3354,7 +3350,7 @@ namespace sat {
} }
case justification::EXT_JUSTIFICATION: { case justification::EXT_JUSTIFICATION: {
literal consequent(var, value(var) == l_false); literal consequent(var, value(var) == l_false);
fill_ext_antecedents(consequent, js); fill_ext_antecedents(consequent, js, false);
for (literal l : m_ext_antecedents) { for (literal l : m_ext_antecedents) {
if (!process_antecedent_for_minimization(l)) { if (!process_antecedent_for_minimization(l)) {
reset_unmark(old_size); reset_unmark(old_size);
@ -3496,7 +3492,7 @@ namespace sat {
break; break;
} }
case justification::EXT_JUSTIFICATION: { case justification::EXT_JUSTIFICATION: {
fill_ext_antecedents(~m_lemma[i], js); fill_ext_antecedents(~m_lemma[i], js, true);
for (literal l : m_ext_antecedents) { for (literal l : m_ext_antecedents) {
update_lrb_reasoned(l); update_lrb_reasoned(l);
} }
@ -3657,6 +3653,7 @@ namespace sat {
s.m_trail_lim = m_trail.size(); s.m_trail_lim = m_trail.size();
s.m_clauses_to_reinit_lim = m_clauses_to_reinit.size(); s.m_clauses_to_reinit_lim = m_clauses_to_reinit.size();
s.m_inconsistent = m_inconsistent; s.m_inconsistent = m_inconsistent;
// m_vars_lim.push(num_vars());
if (m_ext) if (m_ext)
m_ext->push(); m_ext->push();
} }
@ -3668,11 +3665,46 @@ namespace sat {
m_stats.m_units = init_trail_size(); m_stats.m_units = init_trail_size();
} }
void solver::pop_vars(unsigned num_scopes) {
unsigned old_num_vars = m_vars_lim.pop(num_scopes);
if (old_num_vars == num_vars())
return;
IF_VERBOSE(0, verbose_stream() << "new variables created under scope\n";);
for (unsigned v = old_num_vars; v < num_vars(); ++v) {
}
}
void solver::shrink_vars(unsigned v) {
for (bool_var i = v; i < m_justification.size(); ++i) {
m_case_split_queue.del_var_eh(i);
m_probing.reset_cache(literal(i, true));
m_probing.reset_cache(literal(i, false));
}
m_watches.shrink(2*v);
m_assignment.shrink(2*v);
m_justification.shrink(v);
m_decision.shrink(v);
m_eliminated.shrink(v);
m_external.shrink(v);
m_touched.shrink(v);
m_activity.shrink(v);
m_mark.shrink(v);
m_lit_mark.shrink(2*v);
m_phase.shrink(v);
m_best_phase.shrink(v);
m_prev_phase.shrink(v);
m_assigned_since_gc.shrink(v);
m_simplifier.reset_todos();
}
void solver::pop(unsigned num_scopes) { void solver::pop(unsigned num_scopes) {
if (num_scopes == 0) if (num_scopes == 0)
return; return;
if (m_ext) if (m_ext) {
// pop_vars(num_scopes);
m_ext->pop(num_scopes); m_ext->pop(num_scopes);
}
SASSERT(num_scopes <= scope_lvl()); SASSERT(num_scopes <= scope_lvl());
unsigned new_lvl = scope_lvl() - num_scopes; unsigned new_lvl = scope_lvl() - num_scopes;
scope & s = m_scopes[new_lvl]; scope & s = m_scopes[new_lvl];
@ -3709,12 +3741,27 @@ namespace sat {
m_qhead = m_trail.size(); m_qhead = m_trail.size();
if (!m_replay_assign.empty()) IF_VERBOSE(20, verbose_stream() << "replay assign: " << m_replay_assign.size() << "\n"); if (!m_replay_assign.empty()) IF_VERBOSE(20, verbose_stream() << "replay assign: " << m_replay_assign.size() << "\n");
for (unsigned i = m_replay_assign.size(); i-- > 0; ) { for (unsigned i = m_replay_assign.size(); i-- > 0; ) {
m_trail.push_back(m_replay_assign[i]); literal lit = m_replay_assign[i];
if (m_ext && m_external[lit.var()])
m_ext->asserted(lit);
m_trail.push_back(lit);
} }
m_replay_assign.reset(); m_replay_assign.reset();
} }
void solver::get_reinit_literals(unsigned n, literal_vector& r) {
unsigned new_lvl = scope_lvl() - n;
unsigned old_sz = m_scopes[new_lvl].m_clauses_to_reinit_lim;
for (unsigned i = m_clauses_to_reinit.size(); i-- > old_sz; ) {
clause_wrapper cw = m_clauses_to_reinit[i];
for (unsigned j = cw.size(); j-- > 0; )
r.push_back(cw[j]);
}
for (literal lit : m_lemma)
r.push_back(lit);
}
void solver::reinit_clauses(unsigned old_sz) { void solver::reinit_clauses(unsigned old_sz) {
unsigned sz = m_clauses_to_reinit.size(); unsigned sz = m_clauses_to_reinit.size();
SASSERT(old_sz <= sz); SASSERT(old_sz <= sz);
@ -3834,26 +3881,7 @@ namespace sat {
// v is an index of a variable that does not occur in solver state. // v is an index of a variable that does not occur in solver state.
if (v < m_justification.size()) { if (v < m_justification.size()) {
for (bool_var i = v; i < m_justification.size(); ++i) { shrink_vars(v);
m_case_split_queue.del_var_eh(i);
m_probing.reset_cache(literal(i, true));
m_probing.reset_cache(literal(i, false));
}
m_watches.shrink(2*v);
m_assignment.shrink(2*v);
m_justification.shrink(v);
m_decision.shrink(v);
m_eliminated.shrink(v);
m_external.shrink(v);
m_touched.shrink(v);
m_activity.shrink(v);
m_mark.shrink(v);
m_lit_mark.shrink(2*v);
m_phase.shrink(v);
m_best_phase.shrink(v);
m_prev_phase.shrink(v);
m_assigned_since_gc.shrink(v);
m_simplifier.reset_todos();
} }
} }
@ -4766,7 +4794,7 @@ namespace sat {
break; break;
} }
case justification::EXT_JUSTIFICATION: { case justification::EXT_JUSTIFICATION: {
fill_ext_antecedents(lit, js); fill_ext_antecedents(lit, js, true);
for (literal l : m_ext_antecedents) { for (literal l : m_ext_antecedents) {
if (check_domain(lit, l) && all_found) { if (check_domain(lit, l) && all_found) {
s |= m_antecedents.find(l.var()); s |= m_antecedents.find(l.var());

View file

@ -45,6 +45,7 @@ Revision History:
#include "util/trace.h" #include "util/trace.h"
#include "util/rlimit.h" #include "util/rlimit.h"
#include "util/scoped_ptr_vector.h" #include "util/scoped_ptr_vector.h"
#include "util/scoped_limit_trail.h"
namespace sat { namespace sat {
@ -172,6 +173,7 @@ namespace sat {
bool m_inconsistent; bool m_inconsistent;
}; };
svector<scope> m_scopes; svector<scope> m_scopes;
scoped_limit_trail m_vars_lim;
stopwatch m_stopwatch; stopwatch m_stopwatch;
params_ref m_params; params_ref m_params;
scoped_ptr<solver> m_clone; // for debugging purposes scoped_ptr<solver> m_clone; // for debugging purposes
@ -399,6 +401,7 @@ namespace sat {
bool canceled() { return !m_rlimit.inc(); } bool canceled() { return !m_rlimit.inc(); }
config const& get_config() const { return m_config; } config const& get_config() const { return m_config; }
drat& get_drat() { return m_drat; } drat& get_drat() { return m_drat; }
drat* get_drat_ptr() override { return &m_drat; }
void set_incremental(bool b) { m_config.m_incremental = b; } void set_incremental(bool b) { m_config.m_incremental = b; }
bool is_incremental() const { return m_config.m_incremental; } bool is_incremental() const { return m_config.m_incremental; }
extension* get_extension() const override { return m_ext.get(); } extension* get_extension() const override { return m_ext.get(); }
@ -565,8 +568,6 @@ namespace sat {
unsigned m_conflict_lvl; unsigned m_conflict_lvl;
literal_vector m_lemma; literal_vector m_lemma;
literal_vector m_ext_antecedents; literal_vector m_ext_antecedents;
literal m_cached_antecedent_consequent;
ext_justification_idx m_cached_antecedent_js { 0 };
bool use_backjumping(unsigned num_scopes); bool use_backjumping(unsigned num_scopes);
bool resolve_conflict(); bool resolve_conflict();
lbool resolve_conflict_core(); lbool resolve_conflict_core();
@ -582,7 +583,7 @@ namespace sat {
void resolve_conflict_for_unsat_core(); void resolve_conflict_for_unsat_core();
void process_antecedent_for_unsat_core(literal antecedent); void process_antecedent_for_unsat_core(literal antecedent);
void process_consequent_for_unsat_core(literal consequent, justification const& js); void process_consequent_for_unsat_core(literal consequent, justification const& js);
void fill_ext_antecedents(literal consequent, justification js); void fill_ext_antecedents(literal consequent, justification js, bool probing);
unsigned skip_literals_above_conflict_level(); unsigned skip_literals_above_conflict_level();
void updt_phase_of_vars(); void updt_phase_of_vars();
void updt_phase_counters(); void updt_phase_counters();
@ -621,6 +622,8 @@ namespace sat {
void push(); void push();
void pop(unsigned num_scopes); void pop(unsigned num_scopes);
void pop_reinit(unsigned num_scopes); void pop_reinit(unsigned num_scopes);
void shrink_vars(unsigned v);
void pop_vars(unsigned num_scopes);
void unassign_vars(unsigned old_sz, unsigned new_lvl); void unassign_vars(unsigned old_sz, unsigned new_lvl);
void reinit_clauses(unsigned old_sz); void reinit_clauses(unsigned old_sz);
@ -635,6 +638,14 @@ namespace sat {
bool_var max_var(clause_vector& clauses, bool_var v); bool_var max_var(clause_vector& clauses, bool_var v);
bool_var max_var(bool learned, bool_var v); bool_var max_var(bool learned, bool_var v);
// -----------------------
//
// External
//
// -----------------------
public:
void set_should_simplify() { m_next_simplify = m_conflicts_since_init; }
void get_reinit_literals(unsigned num_scopes, literal_vector& r);
public: public:
void user_push() override; void user_push() override;
void user_pop(unsigned num_scopes) override; void user_pop(unsigned num_scopes) override;
@ -798,4 +809,3 @@ namespace sat {
std::ostream & operator<<(std::ostream & out, mk_stat const & stat); std::ostream & operator<<(std::ostream & out, mk_stat const & stat);
}; };

View file

@ -126,7 +126,7 @@ public:
auto* ext = dynamic_cast<euf::solver*>(m_solver.get_extension()); auto* ext = dynamic_cast<euf::solver*>(m_solver.get_extension());
if (ext) { if (ext) {
auto& si = result->m_goal2sat.si(dst_m, m_params, result->m_solver, result->m_map, result->m_dep2asm, is_incremental()); auto& si = result->m_goal2sat.si(dst_m, m_params, result->m_solver, result->m_map, result->m_dep2asm, is_incremental());
euf::solver::scoped_set_translate st(*ext, dst_m, result->m_map, si); euf::solver::scoped_set_translate st(*ext, dst_m, si);
result->m_solver.copy(m_solver); result->m_solver.copy(m_solver);
} }
else { else {

View file

@ -25,6 +25,7 @@ namespace sat {
class cut_simplifier; class cut_simplifier;
class extension; class extension;
class drat;
class solver_core { class solver_core {
protected: protected:
@ -95,6 +96,8 @@ namespace sat {
virtual cut_simplifier* get_cut_simplifier() { return nullptr; } virtual cut_simplifier* get_cut_simplifier() { return nullptr; }
virtual drat* get_drat_ptr() { return nullptr; }
// The following methods are used when converting the state from the SAT solver back // The following methods are used when converting the state from the SAT solver back
// to a set of assertions. // to a set of assertions.

View file

@ -254,19 +254,22 @@ namespace sat {
class status { class status {
public: public:
enum class st { asserted, redundant, deleted }; enum class st { input, asserted, redundant, deleted };
st m_st; st m_st;
int m_orig; int m_orig;
public: public:
status(enum st s, int o) : m_st(s), m_orig(o) {}; status(enum 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 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(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& 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 redundant() { return status(status::st::redundant, -1); }
static status asserted() { return status(status::st::asserted, -1); } static status asserted() { return status(status::st::asserted, -1); }
static status deleted() { return status(status::st::deleted, -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) { return status(redundant ? st::redundant : st::asserted, id); }
bool is_input() const { return st::input == m_st; }
bool is_redundant() const { return st::redundant == m_st; } bool is_redundant() const { return st::redundant == m_st; }
bool is_asserted() const { return st::asserted == m_st; } bool is_asserted() const { return st::asserted == m_st; }
bool is_deleted() const { return st::deleted == m_st; } bool is_deleted() const { return st::deleted == m_st; }
@ -275,6 +278,5 @@ namespace sat {
int get_th() const { return m_orig; } int get_th() const { return m_orig; }
}; };
}; };

View file

@ -3,10 +3,12 @@ z3_add_component(sat_smt
atom2bool_var.cpp atom2bool_var.cpp
ba_internalize.cpp ba_internalize.cpp
ba_solver.cpp ba_solver.cpp
bv_ackerman.cpp
bv_internalize.cpp bv_internalize.cpp
bv_solver.cpp bv_solver.cpp
euf_ackerman.cpp euf_ackerman.cpp
euf_internalize.cpp euf_internalize.cpp
euf_invariant.cpp
euf_model.cpp euf_model.cpp
euf_proof.cpp euf_proof.cpp
euf_solver.cpp euf_solver.cpp

View file

@ -21,6 +21,10 @@ Author:
namespace sat { namespace sat {
void ba_solver::internalize(expr* e, bool redundant) {
internalize(e, false, false, redundant);
}
literal ba_solver::internalize(expr* e, bool sign, bool root, bool redundant) { literal ba_solver::internalize(expr* e, bool sign, bool root, bool redundant) {
flet<bool> _redundant(m_is_redundant, redundant); flet<bool> _redundant(m_is_redundant, redundant);
if (m_pb.is_pb(e)) if (m_pb.is_pb(e))

View file

@ -2107,8 +2107,8 @@ namespace sat {
// ---------------------------- // ----------------------------
// constraint generic methods // constraint generic methods
void ba_solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) { void ba_solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector & r, bool probing) {
get_antecedents(l, index2constraint(idx), r); get_antecedents(l, index2constraint(idx), r, probing);
} }
bool ba_solver::is_watched(literal lit, constraint const& c) const { bool ba_solver::is_watched(literal lit, constraint const& c) const {
@ -2128,14 +2128,14 @@ namespace sat {
get_wlist(~lit).push_back(w); get_wlist(~lit).push_back(w);
} }
void ba_solver::get_antecedents(literal l, constraint const& c, literal_vector& r) { void ba_solver::get_antecedents(literal l, constraint const& c, literal_vector& r, bool probing) {
switch (c.tag()) { switch (c.tag()) {
case card_t: get_antecedents(l, c.to_card(), r); break; case card_t: get_antecedents(l, c.to_card(), r); break;
case pb_t: get_antecedents(l, c.to_pb(), r); break; case pb_t: get_antecedents(l, c.to_pb(), r); break;
case xr_t: get_antecedents(l, c.to_xr(), r); break; case xr_t: get_antecedents(l, c.to_xr(), r); break;
default: UNREACHABLE(); break; default: UNREACHABLE(); break;
} }
if (get_config().m_drat && m_solver) { if (get_config().m_drat && m_solver && !probing) {
literal_vector lits; literal_vector lits;
for (literal lit : r) for (literal lit : r)
lits.push_back(~lit); lits.push_back(~lit);

View file

@ -361,7 +361,7 @@ namespace sat {
void set_conflict(constraint& c, literal lit); void set_conflict(constraint& c, literal lit);
void assign(constraint& c, literal lit); void assign(constraint& c, literal lit);
bool assigned_above(literal above, literal below); bool assigned_above(literal above, literal below);
void get_antecedents(literal l, constraint const& c, literal_vector & r); void get_antecedents(literal l, constraint const& c, literal_vector & r, bool probing);
bool validate_conflict(constraint const& c) const; bool validate_conflict(constraint const& c) const;
bool validate_unit_propagation(constraint const& c, literal alit) const; bool validate_unit_propagation(constraint const& c, literal alit) const;
void validate_eliminated(); void validate_eliminated();
@ -576,8 +576,9 @@ namespace sat {
bool is_external(bool_var v) override; bool is_external(bool_var v) override;
bool propagate(literal l, ext_constraint_idx idx) override; bool propagate(literal l, ext_constraint_idx idx) override;
bool unit_propagate() override { return false; }
lbool resolve_conflict() override; lbool resolve_conflict() override;
void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) override; void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r, bool probing) override;
void asserted(literal l) override; void asserted(literal l) override;
check_result check() override; check_result check() override;
void push() override; void push() override;
@ -604,6 +605,7 @@ namespace sat {
bool check_model(model const& m) const override; bool check_model(model const& m) const override;
literal internalize(expr* e, bool sign, bool root, bool redundant) override; literal internalize(expr* e, bool sign, bool root, bool redundant) override;
void internalize(expr* e, bool redundant) override;
bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override; bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override;
euf::th_solver* fresh(solver* s, euf::solver& ctx) override; euf::th_solver* fresh(solver* s, euf::solver& ctx) override;

153
src/sat/smt/bv_ackerman.cpp Normal file
View file

@ -0,0 +1,153 @@
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
bv_ackerman.cpp
Abstract:
Ackerman reduction plugin for bit-vectors
Author:
Nikolaj Bjorner (nbjorner) 2020-08-28
--*/
#include "sat/smt/bv_solver.h"
#include "sat/smt/bv_ackerman.h"
namespace bv {
ackerman::ackerman(solver& s): s(s) {
new_tmp();
m_propagate_low_watermark = s.get_config().m_dack_threshold;
}
ackerman::~ackerman() {
reset();
dealloc(m_tmp_vv);
}
void ackerman::reset() {
m_table.reset();
m_queue = nullptr;
}
void ackerman::used_eq_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++;
update_glue(*other);
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);
unsigned glue = 0;
unsigned max_glue = v.m_glue;
auto const& bitsa = s.m_bits[v.v1];
auto const& bitsb = s.m_bits[v.v2];
unsigned i = 0;
for (; i < sz && i < max_glue; ++i) {
sat::literal a = bitsa[i];
sat::literal b = bitsb[i];
if (a == b)
continue;
SASSERT(s.s().value(a) != l_undef);
SASSERT(s.s().value(b) == s.s().value(a));
unsigned lvl_a = s.s().lvl(a);
unsigned lvl_b = s.s().lvl(b);
if (!m_diff_levels[lvl_a]) {
m_diff_levels[lvl_a] = true;
++glue;
}
if (!m_diff_levels[lvl_b]) {
m_diff_levels[lvl_b] = true;
++glue;
}
}
for (; i-- > 0; ) {
sat::literal a = bitsa[i];
sat::literal b = bitsb[i];
if (a != b) {
m_diff_levels[s.s().lvl(a)] = false;
m_diff_levels[s.s().lvl(b)] = false;
}
}
if (glue < max_glue)
v.m_glue = glue <= sz ? 0 : glue;
}
void ackerman::remove(vv* p) {
vv::remove_from(m_queue, p);
m_table.erase(p);
dealloc(p);
}
void ackerman::new_tmp() {
m_tmp_vv = alloc(vv);
m_tmp_vv->init(m_tmp_vv);
m_tmp_vv->m_count = 0;
m_tmp_vv->m_glue = UINT_MAX;
}
void ackerman::gc() {
m_num_propagations_since_last_gc++;
if (m_num_propagations_since_last_gc <= s.get_config().m_dack_gc)
return;
m_num_propagations_since_last_gc = 0;
while (m_table.size() > m_gc_threshold)
remove(m_queue->prev());
m_gc_threshold *= 110;
m_gc_threshold /= 100;
m_gc_threshold++;
}
void ackerman::propagate() {
SASSERT(s.s().at_base_lvl());
auto* n = m_queue;
vv* k = nullptr;
unsigned num_prop = static_cast<unsigned>(s.s().get_stats().m_conflict * s.get_config().m_dack_factor);
num_prop = std::min(num_prop, m_table.size());
for (unsigned i = 0; i < num_prop; ++i, n = k) {
k = n->next();
if (n->m_count < m_propagate_low_watermark && n->m_glue != 0)
continue;
add_cc(n->v1, n->v2);
remove(n);
}
}
void ackerman::add_cc(euf::theory_var v1, euf::theory_var v2) {
SASSERT(v1 < v2);
if (static_cast<unsigned>(v2) >= s.get_num_vars())
return;
if (!s.var2enode(v1) || !s.var2enode(v2))
return;
sort* s1 = s.m.get_sort(s.var2expr(v1));
sort* s2 = s.m.get_sort(s.var2expr(v2));
if (s1 != s2 || !s.bv.is_bv_sort(s1))
return;
IF_VERBOSE(0, verbose_stream() << "assert ackerman " << v1 << " " << v2 << "\n");
s.assert_ackerman(v1, v2);
}
}

77
src/sat/smt/bv_ackerman.h Normal file
View file

@ -0,0 +1,77 @@
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
euf_ackerman.h
Abstract:
Ackerman reduction plugin for EUF
Author:
Nikolaj Bjorner (nbjorner) 2020-08-25
--*/
#pragma once
#include "util/dlist.h"
#include "sat/smt/atom2bool_var.h"
#include "sat/smt/sat_th.h"
namespace bv {
class solver;
class ackerman {
struct vv : dll_base<vv> {
euf::theory_var v1, v2;
unsigned m_count{ 0 };
unsigned m_glue{ UINT_MAX };
vv():v1(euf::null_theory_var), v2(euf::null_theory_var) {}
vv(euf::theory_var v1, euf::theory_var v2):v1(v1), v2(v2) {}
};
struct vv_eq {
bool operator()(vv const* a, vv const* b) const {
return a->v1 == b->v1 && a->v2 == b->v2;
}
};
struct vv_hash {
unsigned operator()(vv const* a) const {
return mk_mix(a->v1, a->v2, 0);
}
};
typedef hashtable<vv*, vv_hash, vv_eq> table_t;
solver& s;
table_t m_table;
vv* m_queue { nullptr };
vv* m_tmp_vv { nullptr };
unsigned m_gc_threshold { 1 };
unsigned m_propagate_high_watermark { 10000 };
unsigned m_propagate_low_watermark { 10 };
unsigned m_num_propagations_since_last_gc { 0 };
bool_vector m_diff_levels;
void update_glue(vv& v);
void reset();
void new_tmp();
void remove(vv* inf);
void gc();
void add_cc(euf::theory_var v1, euf::theory_var v2);
public:
ackerman(solver& s);
~ackerman();
void used_eq_eh(euf::theory_var v1, euf::theory_var v2);
void propagate();
};
};

View file

@ -22,7 +22,17 @@ Author:
namespace bv { namespace bv {
class add_var_pos_trail : public trail<euf::solver> { solver::bit_atom& solver::atom::to_bit() {
SASSERT(is_bit());
return dynamic_cast<bit_atom&>(*this);
}
solver::def_atom& solver::atom::to_def() {
SASSERT(!is_bit());
return dynamic_cast<def_atom&>(*this);
}
class solver::add_var_pos_trail : public trail<euf::solver> {
solver::bit_atom* m_atom; solver::bit_atom* m_atom;
public: public:
add_var_pos_trail(solver::bit_atom* a) :m_atom(a) {} add_var_pos_trail(solver::bit_atom* a) :m_atom(a) {}
@ -32,11 +42,11 @@ namespace bv {
} }
}; };
class mk_atom_trail : public trail<euf::solver> { class solver::mk_atom_trail : public trail<euf::solver> {
solver& th; solver& th;
sat::bool_var m_var; sat::bool_var m_var;
public: public:
mk_atom_trail(sat::bool_var v, solver& th):m_var(v), th(th) {} mk_atom_trail(sat::bool_var v, solver& th) : th(th), m_var(v) {}
void undo(euf::solver& euf) override { void undo(euf::solver& euf) override {
solver::atom* a = th.get_bv2a(m_var); solver::atom* a = th.get_bv2a(m_var);
a->~atom(); a->~atom();
@ -51,160 +61,150 @@ namespace bv {
m_wpos.push_back(0); m_wpos.push_back(0);
m_zero_one_bits.push_back(zero_one_bits()); m_zero_one_bits.push_back(zero_one_bits());
ctx.attach_th_var(n, this, r); ctx.attach_th_var(n, this, r);
TRACE("bv", tout << "mk-var: " << r << " " << n->get_owner_id() << " " << mk_pp(n->get_owner(), m) << "\n";);
return r; return r;
} }
void solver::apply_sort_cnstr(euf::enode * n, sort * s) {
get_var(n);
}
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) { sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
SASSERT(m.is_bool(e));
if (!visit_rec(m, e, sign, root, redundant)) if (!visit_rec(m, e, sign, root, redundant))
return sat::null_literal; return sat::null_literal;
return sat::null_literal; return expr2literal(e);
}
void solver::internalize(expr* e, bool redundant) {
visit_rec(m, e, false, false, redundant);
} }
bool solver::visit(expr* e) { bool solver::visit(expr* e) {
if (!bv.is_bv(e)) { if (!is_app(e) || to_app(e)->get_family_id() != get_id()) {
ctx.internalize(e, false, false, m_is_redundant); ctx.internalize(e, m_is_redundant);
return true; return true;
} }
m_args.reset(); m_stack.push_back(sat::eframe(e));
app* a = to_app(e);
for (expr* arg : *a) {
euf::enode* n = get_enode(arg);
if (n)
m_args.push_back(n);
else
m_stack.push_back(sat::eframe(arg));
}
if (m_args.size() != a->get_num_args())
return false; return false;
if (!smt_params().m_bv_reflect)
m_args.reset();
euf::enode* n = mk_enode(a, m_args);
SASSERT(n->interpreted());
theory_var v = n->get_th_var(get_id());
switch (a->get_decl_kind()) {
case OP_BV_NUM: internalize_num(a, v); break;
default: break;
}
return true;
} }
bool solver::visited(expr* e) { bool solver::visited(expr* e) {
return get_enode(e) != nullptr; euf::enode* n = expr2enode(e);
return n && n->is_attached_to(get_id());
}
bool solver::post_visit(expr* e, bool sign, bool root) {
euf::enode* n = expr2enode(e);
app* a = to_app(e);
SASSERT(!n || !n->is_attached_to(get_id()));
if (!n) {
m_args.reset();
if (get_config().m_bv_reflect || m.is_considered_uninterpreted(a->get_decl())) {
for (expr* arg : *a)
m_args.push_back(expr2enode(arg));
}
DEBUG_CODE(for (auto* n : m_args) VERIFY(n););
n = ctx.mk_enode(e, m_args.size(), m_args.c_ptr());
SASSERT(!n->is_attached_to(get_id()));
ctx.attach_node(n);
}
SASSERT(!n->is_attached_to(get_id()));
theory_var v = mk_var(n);
SASSERT(n->is_attached_to(get_id()));
std::function<void(unsigned sz, expr* const* xs, expr* const* ys, expr_ref_vector& bits)> bin;
std::function<void(unsigned sz, expr* const* xs, expr* const* ys, expr_ref& bit)> ebin;
std::function<void(unsigned sz, expr* const* xs, expr_ref_vector& bits)> un;
std::function<void(unsigned sz, expr* const* xs, unsigned p, expr_ref_vector& bits)> pun;
#define internalize_bin(F) bin = [&](unsigned sz, expr* const* xs, expr* const* ys, expr_ref_vector& bits) { m_bb.F(sz, xs, ys, bits); }; internalize_binary(a, bin);
#define internalize_un(F) un = [&](unsigned sz, expr* const* xs, expr_ref_vector& bits) { m_bb.F(sz, xs, bits);}; internalize_unary(a, un);
#define internalize_ac(F) bin = [&](unsigned sz, expr* const* xs, expr* const* ys, expr_ref_vector& bits) { m_bb.F(sz, xs, ys, bits); }; internalize_ac_binary(a, bin);
#define internalize_pun(F) pun = [&](unsigned sz, expr* const* xs, unsigned p, expr_ref_vector& bits) { m_bb.F(sz, xs, p, bits);}; internalize_par_unary(a, pun);
#define internalize_nfl(F) ebin = [&](unsigned sz, expr* const* xs, expr* const* ys, expr_ref& out) { m_bb.F(sz, xs, ys, out);}; internalize_novfl(a, ebin);
switch (a->get_decl_kind()) {
case OP_BV_NUM: internalize_num(a, v); break;
case OP_BNOT: internalize_un(mk_not); break;
case OP_BREDAND: internalize_un(mk_redand); break;
case OP_BREDOR: internalize_un(mk_redor); break;
case OP_BSDIV_I: internalize_bin(mk_sdiv); break;
case OP_BUDIV_I: internalize_bin(mk_udiv); break;
case OP_BUREM_I: internalize_bin(mk_urem); break;
case OP_BSREM_I: internalize_bin(mk_srem); break;
case OP_BSMOD_I: internalize_bin(mk_smod); break;
case OP_BSHL: internalize_bin(mk_shl); break;
case OP_BLSHR: internalize_bin(mk_lshr); break;
case OP_BASHR: internalize_bin(mk_ashr); break;
case OP_EXT_ROTATE_LEFT: internalize_bin(mk_ext_rotate_left); break;
case OP_EXT_ROTATE_RIGHT: internalize_bin(mk_ext_rotate_right); break;
case OP_BADD: internalize_ac(mk_adder); break;
case OP_BMUL: internalize_ac(mk_multiplier); break;
case OP_BAND: internalize_ac(mk_and); break;
case OP_BOR: internalize_ac(mk_or); break;
case OP_BXOR: internalize_ac(mk_xor); break;
case OP_BNAND: internalize_bin(mk_nand); break;
case OP_BNOR: internalize_bin(mk_nor); break;
case OP_BXNOR: internalize_bin(mk_xnor); break;
case OP_BCOMP: internalize_bin(mk_comp); break;
case OP_SIGN_EXT: internalize_pun(mk_sign_extend); break;
case OP_ZERO_EXT: internalize_pun(mk_zero_extend); break;
case OP_ROTATE_LEFT: internalize_pun(mk_rotate_left); break;
case OP_ROTATE_RIGHT: internalize_pun(mk_rotate_right); break;
case OP_BUMUL_NO_OVFL: internalize_nfl(mk_umul_no_overflow); break;
case OP_BSMUL_NO_OVFL: internalize_nfl(mk_smul_no_overflow); break;
case OP_BSMUL_NO_UDFL: internalize_nfl(mk_smul_no_underflow); break;
case OP_BIT2BOOL: internalize_bit2bool(a); break;
case OP_ULEQ: internalize_le<false>(a); break;
case OP_SLEQ: internalize_le<true>(a); break;
case OP_XOR3: internalize_xor3(a); break;
case OP_CARRY: internalize_carry(a); break;
case OP_BSUB: internalize_sub(a); break;
case OP_CONCAT: internalize_concat(a); break;
case OP_EXTRACT: internalize_extract(a); break;
case OP_MKBV: internalize_mkbv(a); break;
case OP_INT2BV: internalize_int2bv(a); break;
case OP_BV2INT: internalize_bv2int(a); break;
case OP_BSDIV0: break;
case OP_BUDIV0: break;
case OP_BSREM0: break;
case OP_BUREM0: break;
case OP_BSMOD0: break;
default:
UNREACHABLE();
break;
}
return true;
} }
void solver::mk_bits(theory_var v) { void solver::mk_bits(theory_var v) {
TRACE("bv", tout << "v" << v << "\n";); TRACE("bv", tout << "v" << v << "\n";);
expr* e = get_expr(v); expr* e = var2expr(v);
unsigned bv_size = get_bv_size(v); unsigned bv_size = get_bv_size(v);
literal_vector& bits = m_bits[v]; m_bits[v].reset();
bits.reset();
for (unsigned i = 0; i < bv_size; i++) { for (unsigned i = 0; i < bv_size; i++) {
expr_ref b2b = mk_bit2bool(e, i); expr_ref b2b(bv.mk_bit2bool(e, i), m);
bits.push_back(ctx.internalize(b2b, false, false, m_is_redundant)); sat::literal lit = ctx.internalize(b2b, false, false, m_is_redundant);
m_bits[v].push_back(lit);
} }
} }
expr_ref solver::mk_bit2bool(expr* e, unsigned idx) {
parameter p(idx);
expr* args[1] = { e };
return expr_ref(m.mk_app(get_id(), OP_BIT2BOOL, 1, &p, 1, args), m);
}
#if 0
SASSERT(!ctx.b_internalized(n));
TRACE("bv", tout << "bit2bool: " << mk_pp(n, ctx.get_manager()) << "\n";);
expr* first_arg = n->get_arg(0);
if (!ctx.e_internalized(first_arg)) {
// This may happen if bit2bool(x) is in a conflict
// clause that is being reinitialized, and x was not reinitialized
// yet.
// So, we internalize x (i.e., arg)
ctx.internalize(first_arg, false);
SASSERT(ctx.e_internalized(first_arg));
// In most cases, when x is internalized, its bits are created.
// They are created because x is a bit-vector operation or apply_sort_cnstr is invoked.
// However, there is an exception. The method apply_sort_cnstr is not invoked for ite-terms.
// So, I execute get_var on the enode attached to first_arg.
// This will force a theory variable to be created if it does not already exist.
// This will also force the creation of all bits for x.
enode* first_arg_enode = ctx.get_enode(first_arg);
get_var(first_arg_enode);
}
enode* arg = ctx.get_enode(first_arg);
// The argument was already internalized, but it may not have a theory variable associated with it.
// For example, for ite-terms the method apply_sort_cnstr is not invoked.
// See comment in the then-branch.
theory_var v_arg = arg->get_th_var(get_id());
if (v_arg == null_theory_var) {
// The method get_var will create a theory variable for arg.
// As a side-effect the bits for arg will also be created.
get_var(arg);
SASSERT(ctx.b_internalized(n));
}
else if (!ctx.b_internalized(n)) {
SASSERT(v_arg != null_theory_var);
bool_var bv = ctx.mk_bool_var(n);
ctx.set_var_theory(bv, get_id());
bit_atom* a = new (get_region()) bit_atom();
insert_bv2a(bv, a);
m_trail_stack.push(mk_atom_trail(bv));
unsigned idx = n->get_decl()->get_parameter(0).get_int();
SASSERT(a->m_occs == 0);
a->m_occs = new (get_region()) var_pos_occ(v_arg, idx);
// Fix for #2182, and SPACER bit-vector
if (idx < m_bits[v_arg].size()) {
ctx.mk_th_axiom(get_id(), m_bits[v_arg][idx], literal(bv, true));
ctx.mk_th_axiom(get_id(), ~m_bits[v_arg][idx], literal(bv, false));
}
}
// axiomatize bit2bool on constants.
rational val;
unsigned sz;
if (m_util.is_numeral(first_arg, val, sz)) {
rational bit;
unsigned idx = n->get_decl()->get_parameter(0).get_int();
div(val, rational::power_of_two(idx), bit);
mod(bit, rational(2), bit);
literal lit = ctx.get_literal(n);
if (bit.is_zero()) lit.neg();
ctx.mark_as_relevant(lit);
ctx.mk_th_axiom(get_id(), 1, &lit);
}
}
#endif
euf::enode * solver::mk_enode(expr * n, ptr_vector<euf::enode> const& args) {
euf::enode * e = ctx.get_enode(n);
if (!e) {
e = ctx.mk_enode(n, args.size(), args.c_ptr());
mk_var(e);
}
return e;
}
euf::theory_var solver::get_var(euf::enode* n) { euf::theory_var solver::get_var(euf::enode* n) {
theory_var v = n->get_th_var(get_id()); theory_var v = n->get_th_var(get_id());
if (v == euf::null_theory_var) { if (v == euf::null_theory_var) {
v = mk_var(n); v = mk_var(n);
if (bv.is_bv(n->get_owner()))
mk_bits(v); mk_bits(v);
} }
return v; return v;
} }
euf::enode* solver::get_arg(euf::enode* n, unsigned idx) { euf::enode* solver::get_arg(euf::enode* n, unsigned idx) {
if (get_config().m_bv_reflect) { app* o = to_app(n->get_owner());
return n->get_arg(idx); return ctx.get_enode(o->get_arg(idx));
}
else {
expr* arg = n->get_arg(idx)->get_owner();
return ctx.get_enode(arg);
}
} }
inline euf::theory_var solver::get_arg_var(euf::enode* n, unsigned idx) { inline euf::theory_var solver::get_arg_var(euf::enode* n, unsigned idx) {
@ -212,20 +212,14 @@ namespace bv {
} }
void solver::get_bits(theory_var v, expr_ref_vector& r) { void solver::get_bits(theory_var v, expr_ref_vector& r) {
literal_vector& bits = m_bits[v]; for (literal lit : m_bits[v])
for (literal lit : bits) { r.push_back(literal2expr(lit));
r.push_back(get_expr(lit));
}
} }
inline void solver::get_bits(euf::enode* n, expr_ref_vector& r) { inline void solver::get_bits(euf::enode* n, expr_ref_vector& r) {
get_bits(get_var(n), r); get_bits(get_var(n), r);
} }
inline void solver::get_arg_bits(euf::enode* n, unsigned idx, expr_ref_vector& r) {
get_bits(get_arg_var(n, idx), r);
}
inline void solver::get_arg_bits(app* n, unsigned idx, expr_ref_vector& r) { inline void solver::get_arg_bits(app* n, unsigned idx, expr_ref_vector& r) {
app* arg = to_app(n->get_arg(idx)); app* arg = to_app(n->get_arg(idx));
get_bits(ctx.get_enode(arg), r); get_bits(ctx.get_enode(arg), r);
@ -242,18 +236,22 @@ namespace bv {
\brief Add bit l to the given variable. \brief Add bit l to the given variable.
*/ */
void solver::add_bit(theory_var v, literal l) { void solver::add_bit(theory_var v, literal l) {
literal_vector & bits = m_bits[v]; unsigned idx = m_bits[v].size();
unsigned idx = bits.size(); m_bits[v].push_back(l);
bits.push_back(l); s().set_external(l.var());
if (s().value(l) != l_undef && s().lvl(l) == 0) { set_bit_eh(v, l, idx);
register_true_false_bit(v, idx);
} }
void solver::set_bit_eh(theory_var v, literal l, unsigned idx) {
if (s().value(l) != l_undef && s().lvl(l) == 0)
register_true_false_bit(v, idx);
else { else {
atom* a = get_bv2a(l.var()); atom* a = get_bv2a(l.var());
SASSERT(!a || a->is_bit()); if (a && !a->is_bit())
if (a) { ;
bit_atom* b = static_cast<bit_atom*>(a); else if (a) {
find_new_diseq_axioms(b->m_occs, v, idx); bit_atom* b = &a->to_bit();
find_new_diseq_axioms(*b, v, idx);
ctx.push(add_var_pos_trail(b)); ctx.push(add_var_pos_trail(b));
b->m_occs = new (get_region()) var_pos_occ(v, idx, b->m_occs); b->m_occs = new (get_region()) var_pos_occ(v, idx, b->m_occs);
} }
@ -261,24 +259,23 @@ namespace bv {
bit_atom* b = new (get_region()) bit_atom(); bit_atom* b = new (get_region()) bit_atom();
insert_bv2a(l.var(), b); insert_bv2a(l.var(), b);
ctx.push(mk_atom_trail(l.var(), *this)); ctx.push(mk_atom_trail(l.var(), *this));
SASSERT(b->m_occs == 0); SASSERT(!b->m_occs);
b->m_occs = new (get_region()) var_pos_occ(v, idx); b->m_occs = new (get_region()) var_pos_occ(v, idx);
} }
} }
} }
void solver::add_unit(sat::literal lit) { void solver::init_bits(expr* e, expr_ref_vector const& bits) {
s().add_clause(1, &lit, status()); euf::enode* n = expr2enode(e);
}
void solver::init_bits(euf::enode * n, expr_ref_vector const & bits) {
SASSERT(get_bv_size(n) == bits.size()); SASSERT(get_bv_size(n) == bits.size());
SASSERT(euf::null_theory_var != n->get_th_var(get_id())); SASSERT(euf::null_theory_var != n->get_th_var(get_id()));
theory_var v = n->get_th_var(get_id()); theory_var v = n->get_th_var(get_id());
unsigned sz = bits.size();
m_bits[v].reset(); m_bits[v].reset();
for (expr* bit : bits) for (expr* bit : bits)
add_bit(v, get_literal(bit)); add_bit(v, ctx.internalize(bit, false, false, m_is_redundant));
for (expr* bit : bits)
get_var(expr2enode(bit));
SASSERT(get_bv_size(n) == bits.size());
find_wpos(v); find_wpos(v);
} }
@ -287,22 +284,23 @@ namespace bv {
} }
unsigned solver::get_bv_size(theory_var v) { unsigned solver::get_bv_size(theory_var v) {
return get_bv_size(get_enode(v)); return get_bv_size(var2enode(v));
} }
void solver::internalize_num(app* n, theory_var v) { void solver::internalize_num(app* n, theory_var v) {
numeral val; numeral val;
unsigned sz = 0; unsigned sz = 0;
SASSERT(expr2enode(n)->interpreted());
VERIFY(bv.is_numeral(n, val, sz)); VERIFY(bv.is_numeral(n, val, sz));
expr_ref_vector bits(m); expr_ref_vector bits(m);
m_bb.num2bits(val, sz, bits); m_bb.num2bits(val, sz, bits);
literal_vector & c_bits = m_bits[v];
SASSERT(bits.size() == sz); SASSERT(bits.size() == sz);
SASSERT(c_bits.empty()); SASSERT(m_bits[v].empty());
sat::literal true_literal = ctx.internalize(m.mk_true(), false, false, false);
for (unsigned i = 0; i < sz; i++) { for (unsigned i = 0; i < sz; i++) {
expr* l = bits.get(i); expr* l = bits.get(i);
SASSERT(m.is_true(l) || m.is_false(l)); SASSERT(m.is_true(l) || m.is_false(l));
c_bits.push_back(m.is_true(l) ? true_literal : false_literal); m_bits[v].push_back(m.is_true(l) ? true_literal : ~true_literal);
register_true_false_bit(v, i); register_true_false_bit(v, i);
} }
fixed_var_eh(v); fixed_var_eh(v);
@ -310,13 +308,11 @@ namespace bv {
void solver::internalize_mkbv(app* n) { void solver::internalize_mkbv(app* n) {
expr_ref_vector bits(m); expr_ref_vector bits(m);
euf::enode * e = mk_enode(n, m_args);
bits.append(n->get_num_args(), n->get_args()); bits.append(n->get_num_args(), n->get_args());
init_bits(e, bits); init_bits(n, bits);
} }
void solver::internalize_bv2int(app* n) { void solver::internalize_bv2int(app* n) {
mk_enode(n, m_args);
assert_bv2int_axiom(n); assert_bv2int_axiom(n);
} }
@ -327,33 +323,27 @@ namespace bv {
void solver::assert_bv2int_axiom(app* n) { void solver::assert_bv2int_axiom(app* n) {
expr* k = nullptr; expr* k = nullptr;
sort * int_sort = m.get_sort(n); VERIFY(bv.is_bv2int(n, k));
SASSERT(bv.is_bv2int(n, k));
SASSERT(bv.is_bv_sort(m.get_sort(k))); SASSERT(bv.is_bv_sort(m.get_sort(k)));
expr_ref_vector k_bits(m); expr_ref_vector k_bits(m);
euf::enode * k_enode = mk_enode(k, m_args); euf::enode* k_enode = expr2enode(k);
get_bits(k_enode, k_bits); get_bits(k_enode, k_bits);
unsigned sz = bv.get_bv_size(k); unsigned sz = bv.get_bv_size(k);
expr_ref_vector args(m); expr_ref_vector args(m);
expr_ref zero(bv.mk_numeral(numeral(0), int_sort), m); expr_ref zero(m_autil.mk_int(0), m);
numeral num(1); unsigned i = 0;
for (expr* b : k_bits) { for (expr* b : k_bits)
expr_ref n(m_autil.mk_numeral(num, int_sort), m); args.push_back(m.mk_ite(b, m_autil.mk_int(power2(i++)), zero));
args.push_back(m.mk_ite(b, n, zero));
num *= numeral(2);
}
expr_ref sum(m_autil.mk_add(sz, args.c_ptr()), m); expr_ref sum(m_autil.mk_add(sz, args.c_ptr()), m);
expr_ref eq(m.mk_eq(n, sum), m); expr_ref eq(m.mk_eq(n, sum), m);
sat::literal lit = ctx.internalize(eq, false, false, m_is_redundant); sat::literal lit = ctx.internalize(eq, false, false, m_is_redundant);
add_unit(lit); add_unit(lit);
} }
void solver::internalize_int2bv(app* n) { void solver::internalize_int2bv(app* n) {
SASSERT(bv.is_int2bv(n)); SASSERT(bv.is_int2bv(n));
euf::enode* e = mk_enode(n, m_args); euf::enode* e = expr2enode(n);
theory_var v = e->get_th_var(get_id()); mk_bits(e->get_th_var(get_id()));
mk_bits(v);
assert_int2bv_axiom(n); assert_int2bv_axiom(n);
} }
@ -367,130 +357,234 @@ namespace bv {
* for i = 0,.., sz-1 * for i = 0,.., sz-1
*/ */
void solver::assert_int2bv_axiom(app* n) { void solver::assert_int2bv_axiom(app* n) {
SASSERT(bv.is_int2bv(n)); expr* e = nullptr;
expr* e = n->get_arg(0); VERIFY(bv.is_int2bv(n, e));
euf::enode* n_enode = mk_enode(n, m_args); euf::enode* n_enode = expr2enode(n);
parameter param(m_autil.mk_int());
expr* n_expr = n;
expr_ref lhs(m), rhs(m); expr_ref lhs(m), rhs(m);
lhs = m.mk_app(get_id(), OP_BV2INT, 1, &param, 1, &n_expr); lhs = bv.mk_bv2int(n);
unsigned sz = bv.get_bv_size(n); unsigned sz = bv.get_bv_size(n);
numeral mod = power(numeral(2), sz); numeral mod = power(numeral(2), sz);
rhs = m_autil.mk_mod(e, m_autil.mk_numeral(mod, true)); rhs = m_autil.mk_mod(e, m_autil.mk_int(mod));
expr_ref eq(m.mk_eq(lhs, rhs), m); expr_ref eq(m.mk_eq(lhs, rhs), m);
literal l = ctx.internalize(eq, false, false, m_is_redundant);
add_unit(l);
TRACE("bv", tout << eq << "\n";); TRACE("bv", tout << eq << "\n";);
add_unit(ctx.internalize(eq, false, false, m_is_redundant));
expr_ref_vector n_bits(m); expr_ref_vector n_bits(m);
get_bits(n_enode, n_bits); get_bits(n_enode, n_bits);
for (unsigned i = 0; i < sz; ++i) { for (unsigned i = 0; i < sz; ++i) {
numeral div = power(numeral(2), i); numeral div = power2(i);
mod = numeral(2); rhs = m_autil.mk_idiv(e, m_autil.mk_int(div));
rhs = m_autil.mk_idiv(e, m_autil.mk_numeral(div, true)); rhs = m_autil.mk_mod(rhs, m_autil.mk_int(2));
rhs = m_autil.mk_mod(rhs, m_autil.mk_numeral(mod, true)); rhs = m.mk_eq(rhs, m_autil.mk_int(1));
rhs = m.mk_eq(rhs, m_autil.mk_numeral(rational(1), true));
lhs = n_bits.get(i); lhs = n_bits.get(i);
expr_ref eq(m.mk_eq(lhs, rhs), m); expr_ref eq(m.mk_eq(lhs, rhs), m);
TRACE("bv", tout << eq << "\n";); TRACE("bv", tout << eq << "\n";);
l = ctx.internalize(eq, false, false, m_is_redundant); add_unit(ctx.internalize(eq, false, false, m_is_redundant));
add_unit(l);
} }
} }
template<bool Signed>
void solver::internalize_add(app* n) {} void solver::internalize_le(app* n) {
void solver::internalize_sub(app* n) {} SASSERT(n->get_num_args() == 2);
void solver::internalize_mul(app* n) {} expr_ref_vector arg1_bits(m), arg2_bits(m);
void solver::internalize_udiv(app* n) {} get_arg_bits(n, 0, arg1_bits);
void solver::internalize_sdiv(app* n) {} get_arg_bits(n, 1, arg2_bits);
void solver::internalize_urem(app* n) {} expr_ref le(m);
void solver::internalize_srem(app* n) {} if (Signed)
void solver::internalize_smod(app* n) {} m_bb.mk_sle(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le);
void solver::internalize_shl(app* n) {} else
void solver::internalize_lshr(app* n) {} m_bb.mk_ule(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le);
void solver::internalize_ashr(app* n) {} literal def = ctx.internalize(le, false, false, m_is_redundant);
void solver::internalize_ext_rotate_left(app* n) {} add_def(def, expr2literal(n));
void solver::internalize_ext_rotate_right(app* n) {}
void solver::internalize_and(app* n) {}
void solver::internalize_or(app* n) {}
void solver::internalize_not(app* n) {}
void solver::internalize_nand(app* n) {}
void solver::internalize_nor(app* n) {}
void solver::internalize_xor(app* n) {}
void solver::internalize_xnor(app* n) {}
void solver::internalize_concat(app* n) {}
void solver::internalize_sign_extend(app* n) {}
void solver::internalize_zero_extend(app* n) {}
void solver::internalize_extract(app* n) {}
void solver::internalize_redand(app* n) {}
void solver::internalize_redor(app* n) {}
void solver::internalize_comp(app* n) {}
void solver::internalize_rotate_left(app* n) {}
void solver::internalize_rotate_right(app* n) {}
void solver::internalize_umul_no_overflow(app* n) {}
void solver::internalize_smul_no_overflow(app* n) {}
void solver::internalize_smul_no_underflow(app* n) {}
} }
void solver::internalize_carry(app* n) {
SASSERT(n->get_num_args() == 3);
#if 0 literal r = ctx.get_literal(n);
literal l1 = ctx.get_literal(n->get_arg(0));
case OP_BADD: internalize_add(term); return true; literal l2 = ctx.get_literal(n->get_arg(1));
case OP_BSUB: internalize_sub(term); return true; literal l3 = ctx.get_literal(n->get_arg(2));
case OP_BMUL: internalize_mul(term); return true; add_clause(~r, l1, l2);
case OP_BSDIV_I: internalize_sdiv(term); return true; add_clause(~r, l1, l3);
case OP_BUDIV_I: internalize_udiv(term); return true; add_clause(~r, l2, l3);
case OP_BSREM_I: internalize_srem(term); return true; add_clause(r, ~l1, ~l2);
case OP_BUREM_I: internalize_urem(term); return true; add_clause(r, ~l1, ~l3);
case OP_BSMOD_I: internalize_smod(term); return true; add_clause(r, ~l2, ~l3);
case OP_BAND: internalize_and(term); return true;
case OP_BOR: internalize_or(term); return true;
case OP_BNOT: internalize_not(term); return true;
case OP_BXOR: internalize_xor(term); return true;
case OP_BNAND: internalize_nand(term); return true;
case OP_BNOR: internalize_nor(term); return true;
case OP_BXNOR: internalize_xnor(term); return true;
case OP_CONCAT: internalize_concat(term); return true;
case OP_SIGN_EXT: internalize_sign_extend(term); return true;
case OP_ZERO_EXT: internalize_zero_extend(term); return true;
case OP_EXTRACT: internalize_extract(term); return true;
case OP_BREDOR: internalize_redor(term); return true;
case OP_BREDAND: internalize_redand(term); return true;
case OP_BCOMP: internalize_comp(term); return true;
case OP_BSHL: internalize_shl(term); return true;
case OP_BLSHR: internalize_lshr(term); return true;
case OP_BASHR: internalize_ashr(term); return true;
case OP_ROTATE_LEFT: internalize_rotate_left(term); return true;
case OP_ROTATE_RIGHT: internalize_rotate_right(term); return true;
case OP_EXT_ROTATE_LEFT: internalize_ext_rotate_left(term); return true;
case OP_EXT_ROTATE_RIGHT: internalize_ext_rotate_right(term); return true;
case OP_BSDIV0: return false;
case OP_BUDIV0: return false;
case OP_BSREM0: return false;
case OP_BUREM0: return false;
case OP_BSMOD0: return false;
case OP_MKBV: internalize_mkbv(term); return true;
case OP_INT2BV:
if (params().m_bv_enable_int2bv2int) {
internalize_int2bv(term);
}
return params().m_bv_enable_int2bv2int;
case OP_BV2INT:
if (params().m_bv_enable_int2bv2int) {
internalize_bv2int(term);
}
return params().m_bv_enable_int2bv2int;
default:
TRACE("bv_op", tout << "unsupported operator: " << mk_ll_pp(term, m) << "\n";);
UNREACHABLE();
return false;
} }
void solver::internalize_xor3(app* n) {
SASSERT(n->get_num_args() == 3);
literal r = ctx.get_literal(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, l3);
add_clause(~r, ~l1, ~l2, l3);
add_clause(~r, ~l1, l2, ~l3);
add_clause(~r, l1, ~l2, ~l3);
add_clause(r, ~l1, l2, l3);
add_clause(r, l1, ~l2, l3);
add_clause(r, l1, l2, ~l3);
add_clause(r, ~l1, ~l2, ~l3);
}
void solver::internalize_unary(app* n, std::function<void(unsigned, expr* const*, expr_ref_vector&)>& fn) {
SASSERT(n->get_num_args() == 1);
expr_ref_vector arg1_bits(m), bits(m);
get_arg_bits(n, 0, arg1_bits);
fn(arg1_bits.size(), arg1_bits.c_ptr(), bits);
init_bits(n, bits);
}
void solver::internalize_par_unary(app* n, std::function<void(unsigned, expr* const*, unsigned p, expr_ref_vector&)>& fn) {
SASSERT(n->get_num_args() == 1);
expr_ref_vector arg1_bits(m), bits(m);
get_arg_bits(n, 0, arg1_bits);
unsigned param = n->get_decl()->get_parameter(0).get_int();
fn(arg1_bits.size(), arg1_bits.c_ptr(), param, bits);
init_bits(n, bits);
}
void solver::internalize_binary(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn) {
SASSERT(n->get_num_args() == 2);
expr_ref_vector arg1_bits(m), arg2_bits(m), bits(m);
get_arg_bits(n, 0, arg1_bits);
get_arg_bits(n, 1, arg2_bits);
SASSERT(arg1_bits.size() == arg2_bits.size());
fn(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), bits);
init_bits(n, bits);
}
void solver::internalize_ac_binary(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn) {
SASSERT(n->get_num_args() >= 2);
expr_ref_vector bits(m), new_bits(m), arg_bits(m);
unsigned i = n->get_num_args() - 1;
get_arg_bits(n, i, bits);
for (; i-- > 0; ) {
arg_bits.reset();
get_arg_bits(n, i, arg_bits);
SASSERT(arg_bits.size() == bits.size());
new_bits.reset();
fn(arg_bits.size(), arg_bits.c_ptr(), bits.c_ptr(), new_bits);
bits.swap(new_bits);
}
init_bits(n, bits);
TRACE("bv_verbose", tout << arg_bits << " " << bits << " " << new_bits << "\n";);
}
void solver::internalize_novfl(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref&)>& fn) {
SASSERT(n->get_num_args() == 2);
expr_ref_vector arg1_bits(m), arg2_bits(m);
get_arg_bits(n, 0, arg1_bits);
get_arg_bits(n, 1, arg2_bits);
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));
}
void solver::add_def(sat::literal def, sat::literal l) {
def_atom* a = new (get_region()) def_atom(l, def);
insert_bv2a(l.var(), a);
ctx.push(mk_atom_trail(l.var(), *this));
add_clause(l, ~def);
add_clause(def, ~l);
}
void solver::internalize_concat(app* n) {
euf::enode* e = expr2enode(n);
theory_var v = e->get_th_var(get_id());
m_bits[v].reset();
for (unsigned i = n->get_num_args(); i-- > 0; )
for (literal lit : m_bits[get_arg_var(e, i)])
add_bit(v, lit);
find_wpos(v);
}
void solver::internalize_sub(app* n) {
SASSERT(n->get_num_args() == 2);
expr_ref_vector arg1_bits(m), arg2_bits(m), bits(m);
get_arg_bits(n, 0, arg1_bits);
get_arg_bits(n, 1, arg2_bits);
SASSERT(arg1_bits.size() == arg2_bits.size());
expr_ref carry(m);
m_bb.mk_subtracter(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), bits, carry);
init_bits(n, bits);
}
void solver::internalize_extract(app* n) {
SASSERT(n->get_num_args() == 1);
euf::enode* e = expr2enode(n);
theory_var v = e->get_th_var(get_id());
theory_var arg = get_arg_var(e, 0);
unsigned start = n->get_decl()->get_parameter(1).get_int();
unsigned end = n->get_decl()->get_parameter(0).get_int();
SASSERT(start <= end && end < get_bv_size(v));
m_bits[v].reset();
for (unsigned i = start; i <= end; ++i)
add_bit(v, m_bits[arg][i]);
find_wpos(v);
}
void solver::internalize_bit2bool(app* n) {
unsigned idx = 0;
expr* arg = nullptr;
VERIFY(bv.is_bit2bool(n, arg, idx));
euf::enode* argn = expr2enode(arg);
if (!argn->is_attached_to(get_id())) {
mk_var(argn);
}
theory_var v_arg = argn->get_th_var(get_id());
sat::literal lit = expr2literal(n);
sat::bool_var b = lit.var();
bit_atom* a = new (get_region()) bit_atom();
SASSERT(idx < get_bv_size(v_arg));
a->m_occs = new (get_region()) var_pos_occ(v_arg, idx);
insert_bv2a(b, a);
ctx.push(mk_atom_trail(b, *this));
if (idx < m_bits[v_arg].size() && lit != m_bits[v_arg][idx]) {
add_clause(m_bits[v_arg][idx], ~lit);
add_clause(~m_bits[v_arg][idx], lit);
}
// axiomatize bit2bool on constants.
rational val;
unsigned sz;
if (bv.is_numeral(arg, val, sz)) {
rational bit;
div(val, rational::power_of_two(idx), bit);
mod(bit, rational(2), bit);
if (bit.is_zero()) lit.neg();
add_unit(lit);
}
}
void solver::assert_ackerman(theory_var v1, theory_var 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);
literal oeq = ctx.internalize(oe, false, false, m_is_redundant);
unsigned sz = get_bv_size(v1);
TRACE("bv", tout << oe << "\n";);
literal_vector eqs;
for (unsigned i = 0; i < sz; ++i) {
literal l1 = m_bits[v1][i];
literal l2 = m_bits[v2][i];
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);
literal eq = ctx.internalize(e, false, false, m_is_redundant);
add_clause(l1, ~l2, ~eq);
add_clause(~l1, l2, ~eq);
add_clause(l1, l2, eq);
add_clause(~l1, ~l2, eq);
add_clause(eq, ~oeq);
eqs.push_back(~eq);
}
eqs.push_back(oeq);
s().add_clause(eqs.size(), eqs.c_ptr(), sat::status::th(m_is_redundant, get_id()));
}
} }
#endif

View file

@ -3,18 +3,20 @@ Copyright (c) 2020 Microsoft Corporation
Module Name: Module Name:
bv_internalize.cpp bv_solver.cpp
Abstract: Abstract:
Internalize utilities for bit-vector solver plugin. Solving utilities for bit-vectors.
Author: Author:
Nikolaj Bjorner (nbjorner) 2020-09-02 Nikolaj Bjorner (nbjorner) 2020-09-02
based on smt/theory_bv
--*/ --*/
#include "ast/ast_ll_pp.h"
#include "sat/smt/bv_solver.h" #include "sat/smt/bv_solver.h"
#include "sat/smt/euf_solver.h" #include "sat/smt/euf_solver.h"
#include "sat/smt/sat_th.h" #include "sat/smt/sat_th.h"
@ -22,8 +24,73 @@ Author:
namespace bv { namespace bv {
void solver::fixed_var_eh(theory_var v) { class solver::bit_trail : public trail<euf::solver> {
solver& s;
solver::var_pos vp;
sat::literal lit;
public:
bit_trail(solver& s, var_pos vp) : s(s), vp(vp), lit(s.m_bits[vp.first][vp.second]) {}
virtual void undo(euf::solver& euf) {
s.m_bits[vp.first][vp.second] = lit;
}
};
solver::solver(euf::solver& ctx, theory_id id) :
euf::th_euf_solver(ctx, id),
bv(m),
m_autil(m),
m_ackerman(*this),
m_bb(m, get_config()),
m_find(*this) {
}
void solver::fixed_var_eh(theory_var v1) {
numeral val1, val2;
VERIFY(get_fixed_value(v1, val1));
unsigned sz = m_bits[v1].size();
value_sort_pair key(val1, sz);
theory_var v2;
bool is_current =
m_fixed_var_table.find(key, v2) &&
v2 < static_cast<int>(get_num_vars()) &&
is_bv(v2) &&
get_bv_size(v2) == sz &&
get_fixed_value(v2, val2) && val1 == val2;
if (!is_current)
m_fixed_var_table.insert(key, v1);
else if (var2enode(v1)->get_root() != var2enode(v2)->get_root()) {
SASSERT(get_bv_size(v1) == get_bv_size(v2));
TRACE("bv", tout << "detected equality: v" << v1 << " = v" << v2 << "\n" << pp(v1) << pp(v2););
m_stats.m_num_th2core_eq++;
add_fixed_eq(v1, v2);
ctx.propagate(var2enode(v1), var2enode(v2), mk_bit2bv_justification(v1, v2));
}
}
void solver::add_fixed_eq(theory_var v1, theory_var v2) {
if (!get_config().m_bv_eq_axioms)
return;
m_ackerman.used_eq_eh(v1, v2);
}
bool solver::get_fixed_value(theory_var v, numeral& result) const {
result.reset();
unsigned i = 0;
for (literal b : m_bits[v]) {
switch (ctx.s().value(b)) {
case l_false:
break;
case l_undef:
return false;
case l_true:
result += power2(i);
break;
}
++i;
}
return true;
} }
/** /**
@ -33,41 +100,34 @@ namespace bv {
literal_vector const& bits = m_bits[v]; literal_vector const& bits = m_bits[v];
unsigned sz = bits.size(); unsigned sz = bits.size();
unsigned& wpos = m_wpos[v]; unsigned& wpos = m_wpos[v];
unsigned init = wpos; for (unsigned i = 0; i < sz; ++i) {
for (; wpos < sz; wpos++) { unsigned idx = (i + wpos) % sz;
TRACE("find_wpos", tout << "curr bit: " << bits[wpos] << "\n";); if (s().value(bits[idx]) == l_undef) {
if (s().value(bits[wpos]) == l_undef) { wpos = idx;
TRACE("find_wpos", tout << "moved wpos of v" << v << " to " << wpos << "\n";); TRACE("bv", tout << "moved wpos of v" << v << " to " << wpos << "\n";);
return; return;
} }
} }
wpos = 0; TRACE("bv", tout << "v" << v << " is a fixed variable.\n";);
for (; wpos < init; wpos++) {
if (s().value(bits[wpos]) == l_undef) {
TRACE("find_wpos", tout << "moved wpos of v" << v << " to " << wpos << "\n";);
return;
}
}
TRACE("find_wpos", tout << "v" << v << " is a fixed variable.\n";);
fixed_var_eh(v); fixed_var_eh(v);
} }
/** /**
\brief v[idx] = ~v'[idx], then v /= v' is a theory axiom. *\brief v[idx] = ~v'[idx], then v /= v' is a theory axiom.
*/ */
void solver::find_new_diseq_axioms(var_pos_occ* occs, theory_var v, unsigned idx) { void solver::find_new_diseq_axioms(bit_atom& a, theory_var v, unsigned idx) {
if (!get_config().m_bv_eq_axioms)
return;
literal l = m_bits[v][idx]; literal l = m_bits[v][idx];
l.neg(); l.neg();
while (occs) { for (auto vp : a) {
theory_var v2 = occs->m_var; theory_var v2 = vp.first;
unsigned idx2 = occs->m_idx; unsigned idx2 = vp.second;
if (idx == idx2 && m_bits[v2][idx2] == l && get_bv_size(v2) == get_bv_size(v)) if (idx == idx2 && m_bits[v2][idx2] == l && get_bv_size(v2) == get_bv_size(v))
mk_new_diseq_axiom(v, v2, idx); mk_new_diseq_axiom(v, v2, idx);
occs = occs->m_next;
} }
} }
/** /**
\brief v1[idx] = ~v2[idx], then v1 /= v2 is a theory axiom. \brief v1[idx] = ~v2[idx], then v1 /= v2 is a theory axiom.
*/ */
@ -78,60 +138,533 @@ namespace bv {
// TBD: disabled until new literal creation is supported // TBD: disabled until new literal creation is supported
return; return;
SASSERT(m_bits[v1][idx] == ~m_bits[v2][idx]); SASSERT(m_bits[v1][idx] == ~m_bits[v2][idx]);
TRACE("bv_solver", tout << "found new diseq axiom\n" << pp(v1) << pp(v2);); TRACE("bv", tout << "found new diseq axiom\n" << pp(v1) << pp(v2););
m_stats.m_num_diseq_static++; m_stats.m_num_diseq_static++;
expr_ref eq(m.mk_eq(get_expr(v1), get_expr(v2)), m); expr_ref eq(m.mk_eq(var2expr(v1), var2expr(v2)), m);
sat::literal neq = ctx.internalize(eq, true, false, m_is_redundant); add_unit(~ctx.internalize(eq, false, false, m_is_redundant));
s().add_clause(1, &neq, sat::status::th(m_is_redundant, get_id()));
} }
std::ostream& solver::display(std::ostream& out, theory_var v) const { std::ostream& solver::display(std::ostream& out, theory_var v) const {
expr* e = var2expr(v);
out << "v"; out << "v";
out.width(4); out.width(4);
out << std::left << v; out << std::left << v;
out << " #"; out << " ";
out.width(4); out.width(4);
out << get_enode(v)->get_owner_id() << " -> #"; out << e->get_id() << " -> ";
out.width(4); out.width(4);
#if 0 out << var2enode(find(v))->get_owner_id();
out << get_enode(find(v))->get_owner_id(); out << std::right;
out << std::right << ", bits:"; out.flush();
literal_vector const& bits = m_bits[v]; atom* a = nullptr;
for (literal lit : bits) { if (is_bv(v)) {
out << " " << lit << ":";
ctx.display_literal(out, lit);
}
numeral val; numeral val;
if (get_fixed_value(v, val)) if (get_fixed_value(v, val))
out << ", value: " << val; out << " (= " << val << ")";
for (literal lit : m_bits[v]) {
out << " " << lit << ":" << mk_bounded_pp(literal2expr(lit), m, 1);
}
}
else if (m.is_bool(e) && (a = m_bool_var2atom.get(expr2literal(e).var(), nullptr))) {
if (a->is_bit()) {
for (var_pos vp : a->to_bit())
out << " " << var2enode(vp.first)->get_owner_id() << "[" << vp.second << "]";
}
else
out << "def-atom";
}
else
out << " " << mk_bounded_pp(e, m, 1);
out << "\n"; out << "\n";
#endif
return out; return out;
} }
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);
}
double solver::get_reward(literal l, sat::ext_constraint_idx idx, sat::literal_occs_fun& occs) const { return 0; } double solver::get_reward(literal l, sat::ext_constraint_idx idx, sat::literal_occs_fun& occs) const { return 0; }
bool solver::is_extended_binary(sat::ext_justification_idx idx, literal_vector& r) { return false; } bool solver::is_extended_binary(sat::ext_justification_idx idx, literal_vector& r) { return false; }
bool solver::is_external(bool_var v) { return true; } bool solver::is_external(bool_var v) { return true; }
bool solver::propagate(literal l, sat::ext_constraint_idx idx) { return false; } bool solver::propagate(literal l, sat::ext_constraint_idx idx) { return false; }
void solver::get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r) {}
void solver::asserted(literal l) {} void solver::get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) {
sat::check_result solver::check() { return sat::check_result::CR_DONE; } auto& c = bv_justification::from_index(idx);
void solver::push() {} TRACE("bv", display_constraint(tout, idx););
void solver::pop(unsigned n) {} switch (c.m_kind) {
case bv_justification::kind_t::bv2bit:
r.push_back(c.m_antecedent);
ctx.add_antecedent(var2enode(c.m_v1), var2enode(c.m_v2));
break;
case bv_justification::kind_t::bit2bv:
SASSERT(m_bits[c.m_v1].size() == m_bits[c.m_v2].size());
for (unsigned i = m_bits[c.m_v1].size(); i-- > 0; ) {
sat::literal a = m_bits[c.m_v1][i];
sat::literal b = m_bits[c.m_v2][i];
SASSERT(a == b || s().value(a) != l_undef);
SASSERT(s().value(a) == s().value(b));
if (a == b)
continue;
if (s().value(a) == l_false) {
a.neg();
b.neg();
}
r.push_back(a);
r.push_back(b);
}
break;
}
if (!probing && ctx.use_drat())
log_drat(c);
}
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);
sat::literal_vector lits;
auto add_bit = [&](sat::literal lit) {
if (s().value(lit) == l_true)
lit.neg();
lits.push_back(lit);
};
switch (c.m_kind) {
case bv_justification::kind_t::bv2bit:
lits.push_back(~leq);
lits.push_back(~c.m_antecedent);
lits.push_back(c.m_consequent);
break;
case bv_justification::kind_t::bit2bv:
lits.push_back(leq);
for (unsigned i = m_bits[c.m_v1].size(); i-- > 0; ) {
sat::literal a = m_bits[c.m_v1][i];
sat::literal b = m_bits[c.m_v2][i];
if (a != b) {
add_bit(a);
add_bit(b);
}
}
break;
}
ctx.get_drat().add(lits, status());
}
void solver::asserted(literal l) {
atom* a = get_bv2a(l.var());
TRACE("bv", tout << "asserted: " << l << "\n";);
if (a->is_bit())
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;
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]);
return true;
}
void solver::propagate_bits(var_pos entry) {
theory_var v1 = entry.first;
unsigned idx = entry.second;
SASSERT(idx < m_bits[v1].size());
if (m_wpos[v1] == idx)
find_wpos(v1);
literal bit1 = m_bits[v1][idx];
lbool val = s().value(bit1);
TRACE("bv", tout << "propagating v" << v1 << " #" << var2enode(v1)->get_owner_id() << "[" << idx << "] = " << val << "\n";);
if (val == l_undef)
return;
if (val == l_false)
bit1.neg();
for (theory_var v2 = m_find.next(v1); v2 != v1 && !s().inconsistent(); v2 = m_find.next(v2)) {
literal bit2 = m_bits[v2][idx];
SASSERT(m_bits[v1][idx] != ~m_bits[v2][idx]);
TRACE("bv", tout << "propagating #" << var2enode(v2)->get_owner_id() << "[" << idx << "] = " << s().value(bit2) << "\n";);
if (val == l_false)
bit2.neg();
if (l_true != s().value(bit2))
assign_bit(bit2, v1, v2, idx, bit1, false);
}
}
sat::check_result solver::check() {
SASSERT(m_prop_queue.size() == m_prop_queue_head);
return sat::check_result::CR_DONE;
}
void solver::push() {
th_euf_solver::lazy_push();
m_prop_queue_lim.push_back(m_prop_queue.size());
}
void solver::pop(unsigned n) {
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);
}
}
void solver::pre_simplify() {} void solver::pre_simplify() {}
void solver::simplify() {}
void solver::simplify() {
m_ackerman.propagate();
}
bool solver::set_root(literal l, literal r) {
atom* a = get_bv2a(l.var());
if (!a || !a->is_bit())
return true;
for (auto vp : a->to_bit()) {
sat::literal l2 = m_bits[vp.first][vp.second];
sat::literal r2 = (l.sign() == l2.sign()) ? r : ~r;
SASSERT(l2.var() == l.var());
ctx.push(bit_trail(*this, vp));
m_bits[vp.first][vp.second] = r2;
set_bit_eh(vp.first, r2, vp.second);
}
return true;
}
/**
* Instantiate Ackerman axioms for bit-vectors that have become equal after roots have been added.
*/
void solver::flush_roots() {
struct eq {
solver& s;
eq(solver& s) :s(s) {}
bool operator()(theory_var v1, theory_var v2) const {
return s.m_bits[v1] == s.m_bits[v2];
}
};
struct hash {
solver& s;
hash(solver& s) :s(s) {}
bool operator()(theory_var v) const {
literal_vector const& a = s.m_bits[v];
return string_hash(reinterpret_cast<char*>(a.c_ptr()), a.size() * sizeof(sat::literal), 3);
}
};
eq eq_proc(*this);
hash hash_proc(*this);
map<theory_var, theory_var, hash, eq> table(hash_proc, eq_proc);
for (unsigned v = 0; v < get_num_vars(); ++v) {
if (!m_bits[v].empty()) {
theory_var w = table.insert_if_not_there(v, v);
if (v != w && m_find.find(v) != m_find.find(w))
assert_ackerman(v, w);
}
}
TRACE("bv", tout << "infer new equations for bit-vectors that are now equal\n";);
}
void solver::clauses_modifed() {} void solver::clauses_modifed() {}
lbool solver::get_phase(bool_var v) { return l_undef; } lbool solver::get_phase(bool_var v) { return l_undef; }
std::ostream& solver::display(std::ostream& out) const { return out; } std::ostream& solver::display(std::ostream& out) const {
std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const { return out; } unsigned num_vars = get_num_vars();
std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const { return out; } if (num_vars > 0)
void solver::collect_statistics(statistics& st) const {} out << "bv-solver:\n";
sat::extension* solver::copy(sat::solver* s) { return nullptr; } for (unsigned v = 0; v < num_vars; v++)
out << pp(v);
return out;
}
std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const {
return display_constraint(out, idx);
}
std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const {
auto& c = bv_justification::from_index(idx);
switch (c.m_kind) {
case bv_justification::kind_t::bv2bit:
return out << c.m_consequent << " <= " << c.m_antecedent << " v" << c.m_v1 << " == v" << c.m_v2 << "\n";
case bv_justification::kind_t::bit2bv:
return out << m_bits[c.m_v1] << " == " << m_bits[c.m_v2] << " => v" << c.m_v1 << " == v" << c.m_v2 << "\n";
default:
UNREACHABLE();
break;
}
return out;
}
void solver::collect_statistics(statistics& st) const {
st.update("bv conflicts", m_stats.m_num_conflicts);
st.update("bv diseqs", m_stats.m_num_diseq_static);
st.update("bv dynamic diseqs", m_stats.m_num_diseq_dynamic);
st.update("bv bit2core", m_stats.m_num_bit2core);
st.update("bv->core eq", m_stats.m_num_th2core_eq);
st.update("bv ackerman", m_stats.m_ackerman);
}
sat::extension* solver::copy(sat::solver* s) { UNREACHABLE(); return nullptr; }
euf::th_solver* solver::fresh(sat::solver* s, euf::solver& ctx) {
bv::solver* result = alloc(bv::solver, ctx, get_id());
ast_translation tr(m, ctx.get_manager());
for (unsigned i = 0; i < get_num_vars(); ++i) {
expr* e1 = var2expr(i);
expr* e2 = tr(e1);
euf::enode* n2 = ctx.get_enode(e2);
SASSERT(n2);
result->mk_var(n2);
result->m_bits[i].append(m_bits[i]);
result->m_zero_one_bits[i].append(m_zero_one_bits[i]);
}
for (unsigned i = 0; i < get_num_vars(); ++i)
if (find(i) != i)
result->m_find.merge(i, find(i));
result->m_prop_queue.append(m_prop_queue);
for (unsigned i = 0; i < m_bool_var2atom.size(); ++i) {
atom* a = m_bool_var2atom[i];
if (!a)
continue;
if (a->is_bit()) {
bit_atom* new_a = new (result->get_region()) bit_atom();
m_bool_var2atom.setx(i, new_a, nullptr);
for (auto vp : a->to_bit())
new_a->m_occs = new (result->get_region()) var_pos_occ(vp.first, vp.second, new_a->m_occs);
}
else {
def_atom* new_a = new (result->get_region()) def_atom(a->to_def().m_var, a->to_def().m_def);
m_bool_var2atom.setx(i, new_a, nullptr);
}
}
return result;
}
void solver::pop_reinit() {} void solver::pop_reinit() {}
bool solver::validate() { return true; } bool solver::validate() { return true; }
void solver::init_use_list(sat::ext_use_list& ul) {} void solver::init_use_list(sat::ext_use_list& ul) {}
bool solver::is_blocked(literal l, sat::ext_constraint_idx) { return false; } bool solver::is_blocked(literal l, sat::ext_constraint_idx) { return false; }
bool solver::check_model(sat::model const& m) const { return true; } bool solver::check_model(sat::model const& m) const { return true; }
unsigned solver::max_var(unsigned w) const { return 0;} unsigned solver::max_var(unsigned w) const { return w; }
void solver::add_value(euf::enode* n, expr_ref_vector& values) {
SASSERT(bv.is_bv(n->get_owner()));
theory_var v = n->get_th_var(get_id());
rational val;
unsigned i = 0;
for (auto l : m_bits[v]) {
switch (s().value(l)) {
case l_true:
val += power2(i);
break;
default:
break;
}
++i;
}
values[n->get_root_id()] = bv.mk_numeral(val, m_bits[v].size());
}
trail_stack<euf::solver>& solver::get_trail_stack() {
return ctx.get_trail_stack();
}
void solver::merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {
TRACE("bv", tout << "merging: v" << v1 << " #" << var2enode(v1)->get_owner_id() << " v" << v2 << " #" << var2enode(v2)->get_owner_id() << "\n";);
if (!merge_zero_one_bits(r1, r2)) {
TRACE("bv", tout << "conflict detected\n";);
return; // conflict was detected
}
SASSERT(m_bits[v1].size() == m_bits[v2].size());
unsigned sz = m_bits[v1].size();
for (unsigned idx = 0; !s().inconsistent() && idx < sz; idx++) {
literal bit1 = m_bits[v1][idx];
literal bit2 = m_bits[v2][idx];
CTRACE("bv", bit1 == ~bit2, tout << pp(v1) << pp(v2) << "idx: " << idx << "\n";);
SASSERT(bit1 != ~bit2);
lbool val1 = s().value(bit1);
lbool val2 = s().value(bit2);
TRACE("bv", tout << "merge v" << v1 << " " << bit1 << ":= " << val1 << " " << bit2 << ":= " << val2 << "\n";);
if (val1 == val2)
continue;
CTRACE("bv", (val1 != l_undef && val2 != l_undef), tout << "inconsistent "; tout << pp(v1) << pp(v2) << "idx: " << idx << "\n";);
if (val1 == l_false)
assign_bit(~bit2, v1, v2, idx, ~bit1, true);
else if (val1 == l_true)
assign_bit(bit2, v1, v2, idx, bit1, true);
else if (val2 == l_false)
assign_bit(~bit1, v2, v1, idx, ~bit2, true);
else if (val2 == l_true)
assign_bit(bit1, v2, v1, idx, bit2, true);
}
}
sat::justification solver::mk_bv2bit_justification(theory_var v1, theory_var v2, sat::literal c, sat::literal a) {
void* mem = get_region().allocate(bv_justification::get_obj_size());
sat::constraint_base::initialize(mem, this);
auto* constraint = new (sat::constraint_base::ptr2mem(mem)) bv_justification(v1, v2, c, a);
return sat::justification::mk_ext_justification(s().scope_lvl(), constraint->to_index());
}
sat::ext_justification_idx solver::mk_bit2bv_justification(theory_var v1, theory_var v2) {
void* mem = get_region().allocate(bv_justification::get_obj_size());
sat::constraint_base::initialize(mem, this);
auto* constraint = new (sat::constraint_base::ptr2mem(mem)) bv_justification(v1, v2);
return constraint->to_index();
}
void solver::assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc) {
m_stats.m_num_bit2core++;
SASSERT(ctx.s().value(antecedent) == l_true);
SASSERT(m_bits[v2][idx].var() == consequent.var());
SASSERT(consequent.var() != antecedent.var());
s().assign(consequent, mk_bv2bit_justification(v1, v2, consequent, antecedent));
if (s().value(consequent) == l_false) {
m_stats.m_num_conflicts++;
SASSERT(s().inconsistent());
}
else {
if (get_config().m_bv_eq_axioms && false) {
// TODO - enable when pop_reinit is available
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);
add_clause(~antecedent, ~eq_lit, consequent);
add_clause(antecedent, ~eq_lit, ~consequent);
}
if (m_wpos[v2] == idx)
find_wpos(v2);
bool_var cv = consequent.var();
atom* a = get_bv2a(cv);
if (a && a->is_bit())
for (auto curr : a->to_bit())
if (propagate_eqc || find(curr.first) != find(v2) || curr.second != idx)
m_prop_queue.push_back(curr);
}
}
void solver::unmerge_eh(theory_var v1, theory_var v2) {
// v1 was the root of the equivalence class
// I must remove the zero_one_bits that are from v2.
zero_one_bits& bits = m_zero_one_bits[v1];
if (bits.empty())
return;
for (unsigned j = bits.size(); j-- > 0; ) {
zero_one_bit& bit = bits[j];
if (find(bit.m_owner) == v1) {
bits.shrink(j + 1);
return;
}
}
bits.shrink(0);
}
bool solver::merge_zero_one_bits(theory_var r1, theory_var r2) {
zero_one_bits& bits2 = m_zero_one_bits[r2];
if (bits2.empty())
return true;
zero_one_bits& bits1 = m_zero_one_bits[r1];
unsigned bv_size = get_bv_size(r1);
SASSERT(bv_size == get_bv_size(r2));
m_merge_aux[0].reserve(bv_size + 1, euf::null_theory_var);
m_merge_aux[1].reserve(bv_size + 1, euf::null_theory_var);
struct scoped_reset {
solver& s;
zero_one_bits& bits1;
scoped_reset(solver& s, zero_one_bits& bits1) :s(s), bits1(bits1) {}
~scoped_reset() {
for (auto& zo : bits1)
s.m_merge_aux[zo.m_is_true][zo.m_idx] = euf::null_theory_var;
}
};
scoped_reset _sr(*this, bits1);
DEBUG_CODE(for (unsigned i = 0; i < bv_size; i++) SASSERT(m_merge_aux[0][i] == euf::null_theory_var || m_merge_aux[1][i] == euf::null_theory_var););
// save info about bits1
for (auto& zo : bits1)
m_merge_aux[zo.m_is_true][zo.m_idx] = zo.m_owner;
// check if bits2 is consistent with bits1, and copy new bits to bits1
for (auto& zo : bits2) {
theory_var v2 = zo.m_owner;
theory_var v1 = m_merge_aux[!zo.m_is_true][zo.m_idx];
if (v1 != euf::null_theory_var) {
// conflict was detected ... v1 and v2 have complementary bits
SASSERT(m_bits[v1][zo.m_idx] == ~(m_bits[v2][zo.m_idx]));
SASSERT(m_bits[v1].size() == m_bits[v2].size());
mk_new_diseq_axiom(v1, v2, zo.m_idx);
return false;
}
// copy missing variable to bits1
if (m_merge_aux[zo.m_is_true][zo.m_idx] == euf::null_theory_var)
bits1.push_back(zo);
}
// reset m_merge_aux vector
DEBUG_CODE(for (unsigned i = 0; i < bv_size; i++) { SASSERT(m_merge_aux[0][i] == euf::null_theory_var || m_merge_aux[1][i] == euf::null_theory_var); });
return true;
}
rational const& solver::power2(unsigned i) const {
while (m_power2.size() <= i)
m_power2.push_back(m_bb.power(m_power2.size()));
return m_power2[i];
}
/**
\brief Check whether m_zero_one_bits is an accurate summary of the bits in the
equivalence class rooted by v.
\remark The method does nothing if v is not the root of the equivalence class.
*/
bool solver::check_zero_one_bits(theory_var v) {
if (s().inconsistent())
return true; // property is only valid if the context is not in a conflict.
if (!is_root(v) || !is_bv(v))
return true;
bool_vector bits[2];
unsigned num_bits = 0;
unsigned bv_sz = get_bv_size(v);
bits[0].resize(bv_sz, false);
bits[1].resize(bv_sz, false);
theory_var curr = v;
do {
literal_vector const& lits = m_bits[curr];
for (unsigned i = 0; i < lits.size(); i++) {
literal l = lits[i];
if (s().value(l) != l_undef) {
unsigned is_true = s().value(l) == l_true;
if (bits[!is_true][i]) {
// expect a conflict later on.
return true;
}
if (!bits[is_true][i]) {
bits[is_true][i] = true;
num_bits++;
}
}
}
curr = m_find.next(curr);
} while (curr != v);
zero_one_bits const& _bits = m_zero_one_bits[v];
SASSERT(_bits.size() == num_bits);
bool_vector already_found;
already_found.resize(bv_sz, false);
for (auto& zo : _bits) {
SASSERT(find(zo.m_owner) == v);
SASSERT(bits[zo.m_is_true][zo.m_idx]);
SASSERT(!already_found[zo.m_idx]);
already_found[zo.m_idx] = true;
}
return true;
}
} }

View file

@ -17,8 +17,13 @@ Author:
#pragma once #pragma once
#include "sat/smt/sat_th.h" #include "sat/smt/sat_th.h"
#include "sat/smt/bv_ackerman.h"
#include "ast/rewriter/bit_blaster/bit_blaster.h" #include "ast/rewriter/bit_blaster/bit_blaster.h"
namespace euf {
class solver;
}
namespace bv { namespace bv {
class solver : public euf::th_euf_solver { class solver : public euf::th_euf_solver {
@ -32,15 +37,42 @@ namespace bv {
typedef std::pair<numeral, unsigned> value_sort_pair; typedef std::pair<numeral, unsigned> value_sort_pair;
typedef pair_hash<obj_hash<numeral>, unsigned_hash> value_sort_pair_hash; typedef pair_hash<obj_hash<numeral>, unsigned_hash> value_sort_pair_hash;
typedef map<value_sort_pair, theory_var, value_sort_pair_hash, default_eq<value_sort_pair> > value2var; typedef map<value_sort_pair, theory_var, value_sort_pair_hash, default_eq<value_sort_pair> > value2var;
typedef union_find<solver> th_union_find; typedef union_find<solver, euf::solver> bv_union_find;
typedef std::pair<theory_var, unsigned> var_pos;
friend class ackerman;
struct stats { struct stats {
unsigned m_num_diseq_static, m_num_diseq_dynamic, m_num_bit2core, m_num_th2core_eq, m_num_conflicts; unsigned m_num_diseq_static, m_num_diseq_dynamic, m_num_bit2core, m_num_th2core_eq, m_num_conflicts;
unsigned m_num_eq_dynamic; unsigned m_ackerman;
void reset() { memset(this, 0, sizeof(stats)); } void reset() { memset(this, 0, sizeof(stats)); }
stats() { reset(); } stats() { reset(); }
}; };
struct bv_justification {
enum kind_t { bv2bit, bit2bv };
kind_t m_kind;
theory_var m_v1;
theory_var m_v2;
sat::literal m_consequent;
sat::literal m_antecedent;
bv_justification(theory_var v1, theory_var v2, sat::literal c, sat::literal a) :
m_kind(kind_t::bv2bit), m_v1(v1), m_v2(v2), m_consequent(c), m_antecedent(a) {}
bv_justification(theory_var v1, theory_var v2):
m_kind(kind_t::bit2bv), m_v1(v1), m_v2(v2) {}
sat::ext_constraint_idx to_index() const {
return sat::constraint_base::mem2base(this);
}
static bv_justification& from_index(size_t idx) {
return *reinterpret_cast<bv_justification*>(sat::constraint_base::from_index(idx)->mem());
}
static size_t get_obj_size() { return sat::constraint_base::obj_size(sizeof(bv_justification)); }
};
sat::justification mk_bv2bit_justification(theory_var v1, theory_var v2, sat::literal c, sat::literal a);
sat::ext_justification_idx mk_bit2bv_justification(theory_var v1, theory_var v2);
void log_drat(bv_justification const& c);
/** /**
\brief Structure used to store the position of a bitvector variable that \brief Structure used to store the position of a bitvector variable that
@ -66,17 +98,31 @@ namespace bv {
typedef svector<zero_one_bit> zero_one_bits; typedef svector<zero_one_bit> zero_one_bits;
struct bit_atom;
struct def_atom;
class atom { class atom {
public: public:
virtual ~atom() {} virtual ~atom() {}
virtual bool is_bit() const = 0; virtual bool is_bit() const = 0;
bit_atom& to_bit();
def_atom& to_def();
}; };
struct var_pos_occ { struct var_pos_occ {
theory_var m_var; var_pos m_vp;
unsigned m_idx;
var_pos_occ * m_next; var_pos_occ * m_next;
var_pos_occ(theory_var v = euf::null_theory_var, unsigned idx = 0, var_pos_occ * next = nullptr):m_var(v), m_idx(idx), m_next(next) {} var_pos_occ(theory_var v = euf::null_theory_var, unsigned idx = 0, var_pos_occ * next = nullptr):m_vp(v, idx), m_next(next) {}
};
class var_pos_it {
var_pos_occ* m_first;
public:
var_pos_it(var_pos_occ* c) : m_first(c) {}
var_pos operator*() { return m_first->m_vp; }
var_pos_it& operator++() { m_first = m_first->m_next; return *this; }
var_pos_it operator++(int) { var_pos_it tmp = *this; ++* this; return tmp; }
bool operator==(var_pos_it const& other) const { return m_first == other.m_first; }
bool operator!=(var_pos_it const& other) const { return !(*this == other); }
}; };
struct bit_atom : public atom { struct bit_atom : public atom {
@ -84,44 +130,51 @@ namespace bv {
bit_atom():m_occs(nullptr) {} bit_atom():m_occs(nullptr) {}
~bit_atom() override {} ~bit_atom() override {}
bool is_bit() const override { return true; } bool is_bit() const override { return true; }
var_pos_it begin() const { return var_pos_it(m_occs); }
var_pos_it end() const { return var_pos_it(nullptr); }
}; };
struct le_atom : public atom { struct def_atom : public atom {
literal m_var; literal m_var;
literal m_def; literal m_def;
le_atom(literal v, literal d):m_var(v), m_def(d) {} def_atom(literal v, literal d):m_var(v), m_def(d) {}
~le_atom() override {} ~def_atom() override {}
bool is_bit() const override { return false; } bool is_bit() const override { return false; }
}; };
friend class add_var_pos_trail; class bit_trail;
friend class mk_atom_trail; class add_var_pos_trail;
class mk_atom_trail;
typedef ptr_vector<atom> bool_var2atom; typedef ptr_vector<atom> bool_var2atom;
bv_util bv; bv_util bv;
arith_util m_autil; arith_util m_autil;
stats m_stats; stats m_stats;
ackerman m_ackerman;
bit_blaster m_bb; bit_blaster m_bb;
th_union_find m_find; bv_union_find m_find;
vector<literal_vector> m_bits; // per var, the bits of a given variable. vector<literal_vector> m_bits; // per var, the bits of a given variable.
ptr_vector<expr> m_bits_expr; unsigned_vector m_wpos; // per var, watch position for fixed variable detection.
svector<unsigned> m_wpos; // per var, watch position for fixed variable detection.
vector<zero_one_bits> m_zero_one_bits; // per var, see comment in the struct zero_one_bit vector<zero_one_bits> m_zero_one_bits; // per var, see comment in the struct zero_one_bit
bool_var2atom m_bool_var2atom; bool_var2atom m_bool_var2atom;
value2var m_fixed_var_table;
mutable vector<rational> m_power2;
literal_vector m_tmp_literals;
svector<var_pos> m_prop_queue;
unsigned_vector m_prop_queue_lim;
unsigned m_prop_queue_head { 0 };
sat::solver* m_solver; sat::solver* m_solver;
sat::solver& s() { return *m_solver; } sat::solver& s() { return *m_solver; }
// internalize: // internalize
void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.setx(bv, a, 0); } void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.setx(bv, a, 0); }
void erase_bv2a(bool_var bv) { m_bool_var2atom[bv] = 0; } void erase_bv2a(bool_var bv) { m_bool_var2atom[bv] = 0; }
atom * get_bv2a(bool_var bv) const { return m_bool_var2atom.get(bv, 0); } atom * get_bv2a(bool_var bv) const { return m_bool_var2atom.get(bv, 0); }
sat::literal false_literal;
sat::literal true_literal;
bool visit(expr* e) override; bool visit(expr* e) override;
bool visited(expr* e) override; bool visited(expr* e) override;
bool post_visit(expr* e, bool sign, bool root) override { return true; } bool post_visit(expr* e, bool sign, bool root) override;
unsigned get_bv_size(euf::enode* n); unsigned get_bv_size(euf::enode* n);
unsigned get_bv_size(theory_var v); unsigned get_bv_size(theory_var v);
theory_var get_var(euf::enode* n); theory_var get_var(euf::enode* n);
@ -129,66 +182,55 @@ namespace bv {
inline theory_var get_arg_var(euf::enode* n, unsigned idx); inline theory_var get_arg_var(euf::enode* n, unsigned idx);
void get_bits(theory_var v, expr_ref_vector& r); void get_bits(theory_var v, expr_ref_vector& r);
void get_bits(euf::enode* n, expr_ref_vector& r); void get_bits(euf::enode* n, expr_ref_vector& r);
void get_arg_bits(euf::enode* n, unsigned idx, expr_ref_vector& r);
void get_arg_bits(app* n, unsigned idx, expr_ref_vector& r); void get_arg_bits(app* n, unsigned idx, expr_ref_vector& r);
euf::enode* mk_enode(expr* n, ptr_vector<euf::enode> const& args);
void fixed_var_eh(theory_var v); void fixed_var_eh(theory_var v);
bool is_bv(theory_var v) const { return bv.is_bv(var2expr(v)); }
sat::status status() const { return sat::status::th(m_is_redundant, get_id()); } sat::status status() const { return sat::status::th(m_is_redundant, get_id()); }
void add_unit(sat::literal lit);
void register_true_false_bit(theory_var v, unsigned i); void register_true_false_bit(theory_var v, unsigned i);
void add_bit(theory_var v, sat::literal lit); void add_bit(theory_var v, sat::literal lit);
void init_bits(euf::enode * n, expr_ref_vector const & bits); void set_bit_eh(theory_var v, literal l, unsigned idx);
void init_bits(expr* e, expr_ref_vector const & bits);
void mk_bits(theory_var v); void mk_bits(theory_var v);
expr_ref mk_bit2bool(expr* b, unsigned idx); void add_def(sat::literal def, sat::literal l);
void internalize_unary(app* n, std::function<void(unsigned, expr* const*, expr_ref_vector&)>& fn);
void internalize_binary(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn);
void internalize_ac_binary(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn);
void internalize_par_unary(app* n, std::function<void(unsigned, expr* const*, unsigned p, expr_ref_vector&)>& fn);
void internalize_novfl(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref&)>& fn);
void internalize_num(app * n, theory_var v); void internalize_num(app * n, theory_var v);
void internalize_add(app * n);
void internalize_sub(app * n);
void internalize_mul(app * n);
void internalize_udiv(app * n);
void internalize_sdiv(app * n);
void internalize_urem(app * n);
void internalize_srem(app * n);
void internalize_smod(app * n);
void internalize_shl(app * n);
void internalize_lshr(app * n);
void internalize_ashr(app * n);
void internalize_ext_rotate_left(app * n);
void internalize_ext_rotate_right(app * n);
void internalize_and(app * n);
void internalize_or(app * n);
void internalize_not(app * n);
void internalize_nand(app * n);
void internalize_nor(app * n);
void internalize_xor(app * n);
void internalize_xnor(app * n);
void internalize_concat(app * n); void internalize_concat(app * n);
void internalize_sign_extend(app * n);
void internalize_zero_extend(app * n);
void internalize_extract(app * n);
void internalize_redand(app * n);
void internalize_redor(app * n);
void internalize_comp(app * n);
void internalize_rotate_left(app * n);
void internalize_rotate_right(app * n);
void internalize_bv2int(app* n); void internalize_bv2int(app* n);
void internalize_int2bv(app* n); void internalize_int2bv(app* n);
void internalize_mkbv(app* n); void internalize_mkbv(app* n);
void internalize_umul_no_overflow(app *n); void internalize_xor3(app* n);
void internalize_smul_no_overflow(app *n); void internalize_carry(app* n);
void internalize_smul_no_underflow(app *n); void internalize_sub(app* n);
void internalize_extract(app* n);
void internalize_bit2bool(app* n);
template<bool Signed>
void internalize_le(app* n);
void assert_bv2int_axiom(app * n); void assert_bv2int_axiom(app * n);
void assert_int2bv_axiom(app* n); void assert_int2bv_axiom(app* n);
void assert_ackerman(theory_var v1, theory_var v2);
// solving // solving
theory_var find(theory_var v) const { return m_find.find(v); }
void find_wpos(theory_var v); void find_wpos(theory_var v);
void find_new_diseq_axioms(var_pos_occ* occs, theory_var v, unsigned idx); void find_new_diseq_axioms(bit_atom& a, theory_var v, unsigned idx);
void mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx); void mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx);
bool get_fixed_value(theory_var v, numeral& result) const;
void add_fixed_eq(theory_var v1, theory_var v2);
svector<theory_var> m_merge_aux[2]; //!< auxiliary vector used in merge_zero_one_bits
bool merge_zero_one_bits(theory_var r1, theory_var r2);
void assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc);
void propagate_bits(var_pos entry);
numeral const& power2(unsigned i) const;
// invariants
bool check_zero_one_bits(theory_var v);
public: public:
solver(euf::solver& ctx); solver(euf::solver& ctx, theory_id id);
~solver() override {} ~solver() override {}
void set_solver(sat::solver* s) override { m_solver = s; } void set_solver(sat::solver* s) override { m_solver = s; }
void set_lookahead(sat::lookahead* s) override { } void set_lookahead(sat::lookahead* s) override { }
@ -197,19 +239,22 @@ namespace bv {
bool is_extended_binary(sat::ext_justification_idx idx, literal_vector& r) override; bool is_extended_binary(sat::ext_justification_idx idx, literal_vector& r) override;
bool is_external(bool_var v) override; bool is_external(bool_var v) override;
bool propagate(literal l, sat::ext_constraint_idx idx) override; bool propagate(literal l, sat::ext_constraint_idx idx) override;
void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector & r) override; void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector & r, bool probing) override;
void asserted(literal l) override; void asserted(literal l) override;
sat::check_result check() override; sat::check_result check() override;
void push() override; void push() override;
void pop(unsigned n) override; void pop(unsigned n) override;
void pre_simplify() override; void pre_simplify() override;
void simplify() override; void simplify() override;
bool set_root(literal l, literal r) override;
void flush_roots() override;
void clauses_modifed() override; void clauses_modifed() override;
lbool get_phase(bool_var v) override; lbool get_phase(bool_var v) override;
std::ostream& display(std::ostream& out) const 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_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; std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override;
void collect_statistics(statistics& st) const override; void collect_statistics(statistics& st) const override;
euf::th_solver* fresh(sat::solver* s, euf::solver& ctx) override;
extension* copy(sat::solver* s) override; extension* copy(sat::solver* s) override;
void find_mutexes(literal_vector& lits, vector<literal_vector> & mutexes) override {} void find_mutexes(literal_vector& lits, vector<literal_vector> & mutexes) override {}
void gc() override {} void gc() override {}
@ -220,18 +265,31 @@ namespace bv {
bool check_model(sat::model const& m) const override; bool check_model(sat::model const& m) const override;
unsigned max_var(unsigned w) const override; unsigned max_var(unsigned w) const override;
void new_eq_eh(euf::th_eq const& eq) override;
bool unit_propagate() override;
void add_value(euf::enode* n, expr_ref_vector& values) override;
bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card, bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card,
std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) override { return false; } std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) override { return false; }
bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override { return false; } bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override { return false; }
sat::literal internalize(expr* e, bool sign, bool root, bool learned) override; sat::literal internalize(expr* e, bool sign, bool root, bool learned) override;
void internalize(expr* e, bool redundant) override;
euf::theory_var mk_var(euf::enode* n) override; euf::theory_var mk_var(euf::enode* n) override;
void apply_sort_cnstr(euf::enode * n, sort * s) override;
void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2);
void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { SASSERT(check_zero_one_bits(r1)); }
void unmerge_eh(theory_var v1, theory_var v2);
trail_stack<euf::solver>& get_trail_stack();
// disagnostics // disagnostics
std::ostream& display(std::ostream& out, theory_var v) const; std::ostream& display(std::ostream& out, theory_var v) const;
typedef std::pair<solver const*, theory_var> pp_var; typedef std::pair<solver const*, theory_var> pp_var;
pp_var pp(theory_var v) const { return pp_var(this, v); } pp_var pp(theory_var v) const { return pp_var(this, v); }
}; };
inline std::ostream& operator<<(std::ostream& out, solver::pp_var const& p) { return p.first->display(out, p.second); } inline std::ostream& operator<<(std::ostream& out, solver::pp_var const& p) { return p.first->display(out, p.second); }

View file

@ -58,37 +58,10 @@ namespace euf {
inf.b = b; inf.b = b;
inf.c = nullptr; inf.c = nullptr;
inf.is_cc = true; inf.is_cc = true;
inf.m_count = 0;
insert(); insert();
} }
void ackerman::remove_from_queue(inference* inf) {
if (m_queue->m_next == m_queue) {
SASSERT(inf == m_queue);
m_queue = nullptr;
return;
}
if (m_queue == inf)
m_queue = inf->m_next;
auto* next = inf->m_next;
auto* prev = inf->m_prev;
prev->m_next = next;
next->m_prev = prev;
}
void ackerman::push_to_front(inference* inf) {
if (!m_queue) {
m_queue = inf;
}
else if (m_queue != inf) {
auto* next = inf->m_next;
auto* prev = inf->m_prev;
prev->m_next = next;
next->m_prev = prev;
inf->m_prev = m_queue->m_prev;
inf->m_next = m_queue;
m_queue->m_prev = inf;
}
}
void ackerman::insert() { void ackerman::insert() {
inference* inf = m_tmp_inference; inference* inf = m_tmp_inference;
@ -97,15 +70,16 @@ namespace euf {
m.inc_ref(inf->a); m.inc_ref(inf->a);
m.inc_ref(inf->b); m.inc_ref(inf->b);
m.inc_ref(inf->c); m.inc_ref(inf->c);
}
else
new_tmp(); new_tmp();
}
other->m_count++; other->m_count++;
push_to_front(other); if (other->m_count > m_high_watermark)
s.s().set_should_simplify();
inference::push_to_front(m_queue, other);
} }
void ackerman::remove(inference* inf) { void ackerman::remove(inference* inf) {
remove_from_queue(inf); inference::remove_from(m_queue, inf);
m_table.erase(inf); m_table.erase(inf);
m.dec_ref(inf->a); m.dec_ref(inf->a);
m.dec_ref(inf->b); m.dec_ref(inf->b);
@ -115,13 +89,10 @@ namespace euf {
void ackerman::new_tmp() { void ackerman::new_tmp() {
m_tmp_inference = alloc(inference); m_tmp_inference = alloc(inference);
m_tmp_inference->m_next = m_tmp_inference->m_prev = m_tmp_inference; m_tmp_inference->init(m_tmp_inference);
m_tmp_inference->m_count = 0;
} }
void ackerman::cg_conflict_eh(expr * n1, expr * n2) { void ackerman::cg_conflict_eh(expr * n1, expr * n2) {
if (s.m_config.m_dack != DACK_ROOT)
return;
if (!is_app(n1) || !is_app(n2)) if (!is_app(n1) || !is_app(n2))
return; return;
app* a = to_app(n1); app* a = to_app(n1);
@ -133,8 +104,6 @@ namespace euf {
} }
void ackerman::used_eq_eh(expr* a, expr* b, expr* c) { void ackerman::used_eq_eh(expr* a, expr* b, expr* c) {
if (!s.m_config.m_dack_eq)
return;
if (a == b || a == c || b == c) if (a == b || a == c || b == c)
return; return;
insert(a, b, c); insert(a, b, c);
@ -142,8 +111,6 @@ namespace euf {
} }
void ackerman::used_cc_eh(app* a, app* b) { void ackerman::used_cc_eh(app* a, app* b) {
if (s.m_config.m_dack != DACK_CR)
return;
SASSERT(a->get_decl() == b->get_decl()); SASSERT(a->get_decl() == b->get_decl());
SASSERT(a->get_num_args() == b->get_num_args()); SASSERT(a->get_num_args() == b->get_num_args());
insert(a, b); insert(a, b);
@ -157,7 +124,7 @@ namespace euf {
m_num_propagations_since_last_gc = 0; m_num_propagations_since_last_gc = 0;
while (m_table.size() > m_gc_threshold) while (m_table.size() > m_gc_threshold)
remove(m_queue->m_prev); remove(m_queue->prev());
m_gc_threshold *= 110; m_gc_threshold *= 110;
m_gc_threshold /= 100; m_gc_threshold /= 100;
@ -171,13 +138,16 @@ namespace euf {
unsigned num_prop = static_cast<unsigned>(s.s().get_stats().m_conflict * s.m_config.m_dack_factor); unsigned num_prop = static_cast<unsigned>(s.s().get_stats().m_conflict * s.m_config.m_dack_factor);
num_prop = std::min(num_prop, m_table.size()); num_prop = std::min(num_prop, m_table.size());
for (unsigned i = 0; i < num_prop; ++i, n = k) { for (unsigned i = 0; i < num_prop; ++i, n = k) {
k = n->m_next; k = n->next();
if (n->m_count < s.m_config.m_dack_threshold) if (n->m_count < s.m_config.m_dack_threshold)
continue; continue;
if (n->m_count >= m_high_watermark && num_prop < m_table.size())
++num_prop;
if (n->is_cc) if (n->is_cc)
add_cc(n->a, n->b); add_cc(n->a, n->b);
else else
add_eq(n->a, n->b, n->c); add_eq(n->a, n->b, n->c);
++s.m_stats.m_ackerman;
remove(n); remove(n);
} }
} }

View file

@ -16,6 +16,7 @@ Author:
--*/ --*/
#pragma once #pragma once
#include "util/dlist.h"
#include "ast/euf/euf_egraph.h" #include "ast/euf/euf_egraph.h"
#include "sat/smt/atom2bool_var.h" #include "sat/smt/atom2bool_var.h"
#include "sat/smt/sat_th.h" #include "sat/smt/sat_th.h"
@ -24,13 +25,12 @@ namespace euf {
class solver; class solver;
class ackerman { class ackerman {
struct inference { struct inference : dll_base<inference>{
bool is_cc; bool is_cc;
expr* a, *b, *c; expr* a, *b, *c;
inference* m_next{ nullptr };
inference* m_prev{ nullptr };
unsigned m_count{ 0 }; unsigned m_count{ 0 };
inference():is_cc(false), a(nullptr), b(nullptr), c(nullptr) {} inference():is_cc(false), a(nullptr), b(nullptr), c(nullptr) {}
inference(app* a, app* b):is_cc(true), a(a), b(b), c(nullptr) {} inference(app* a, app* b):is_cc(true), a(a), b(b), c(nullptr) {}
@ -58,6 +58,7 @@ namespace euf {
inference* m_queue { nullptr }; inference* m_queue { nullptr };
inference* m_tmp_inference { nullptr }; inference* m_tmp_inference { nullptr };
unsigned m_gc_threshold { 1 }; unsigned m_gc_threshold { 1 };
unsigned m_high_watermark { 1000 };
unsigned m_num_propagations_since_last_gc { 0 }; unsigned m_num_propagations_since_last_gc { 0 };
void reset(); void reset();
@ -69,8 +70,6 @@ namespace euf {
void add_cc(expr* a, expr* b); void add_cc(expr* a, expr* b);
void add_eq(expr* a, expr* b, expr* c); void add_eq(expr* a, expr* b, expr* c);
void gc(); void gc();
void push_to_front(inference* inf);
void remove_from_queue(inference* inf);
public: public:
ackerman(solver& s, ast_manager& m); ackerman(solver& s, ast_manager& m);

View file

@ -21,17 +21,27 @@ Author:
namespace euf { namespace euf {
void solver::internalize(expr* e, bool redundant) {
if (si.is_bool_op(e))
attach_lit(si.internalize(e, redundant), e);
else if (auto* ext = get_solver(e))
ext->internalize(e, redundant);
else
visit_rec(m, e, false, false, redundant);
SASSERT(m_egraph.find(e));
}
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) { sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
if (si.is_bool_op(e)) if (si.is_bool_op(e))
return si.internalize(e, redundant); return attach_lit(si.internalize(e, redundant), e);
auto* ext = get_solver(e); if (auto* ext = get_solver(e))
if (ext)
return ext->internalize(e, sign, root, redundant); return ext->internalize(e, sign, root, redundant);
if (!visit_rec(m, e, sign, root, redundant)) if (!visit_rec(m, e, sign, root, redundant))
return sat::null_literal; return sat::null_literal;
SASSERT(m_egraph.find(e)); SASSERT(m_egraph.find(e));
return literal(m_expr2var.to_bool_var(e), sign); if (m.is_bool(e))
return literal(si.to_bool_var(e), sign);
return sat::null_literal;
} }
bool solver::visit(expr* e) { bool solver::visit(expr* e) {
@ -39,15 +49,7 @@ namespace euf {
if (n) if (n)
return true; return true;
if (si.is_bool_op(e)) { if (si.is_bool_op(e)) {
sat::literal lit = si.internalize(e, m_is_redundant); attach_lit(si.internalize(e, m_is_redundant), e);
n = m_var2node.get(lit.var(), nullptr);
if (n && !lit.sign())
return n;
n = m_egraph.mk(e, 0, nullptr);
attach_lit(lit, n);
if (!m.is_true(e) && !m.is_false(e))
s().set_external(lit.var());
return true; return true;
} }
if (is_app(e) && to_app(e)->get_num_args() > 0) { if (is_app(e) && to_app(e)->get_num_args() > 0) {
@ -64,8 +66,12 @@ namespace euf {
m_args.reset(); m_args.reset();
for (unsigned i = 0; i < num; ++i) for (unsigned i = 0; i < num; ++i)
m_args.push_back(m_egraph.find(to_app(e)->get_arg(i))); m_args.push_back(m_egraph.find(to_app(e)->get_arg(i)));
if (root && internalize_root(to_app(e), sign)) if (root && internalize_root(to_app(e), sign, m_args))
return false; return false;
if (auto* s = get_solver(e)) {
s->internalize(e, m_is_redundant);
return true;
}
enode* n = m_egraph.mk(e, num, m_args.c_ptr()); enode* n = m_egraph.mk(e, num, m_args.c_ptr());
attach_node(n); attach_node(n);
return true; return true;
@ -77,33 +83,45 @@ namespace euf {
void solver::attach_node(euf::enode* n) { void solver::attach_node(euf::enode* n) {
expr* e = n->get_owner(); expr* e = n->get_owner();
log_node(n); if (!m.is_bool(e))
if (m.is_bool(e)) { log_node(e);
sat::bool_var v = si.add_bool_var(e); else
log_bool_var(v, n); attach_lit(literal(si.add_bool_var(e), false), e);
attach_lit(literal(v, false), n);
if (!m.is_bool(e) && m.get_sort(e)->get_family_id() != null_family_id) {
auto* e_ext = get_solver(e);
auto* s_ext = get_solver(m.get_sort(e));
if (s_ext && s_ext != e_ext)
s_ext->apply_sort_cnstr(n, m.get_sort(e));
} }
axiomatize_basic(n); axiomatize_basic(n);
} }
void solver::attach_lit(literal lit, euf::enode* n) { sat::literal solver::attach_lit(literal lit, expr* e) {
if (lit.sign()) { if (lit.sign()) {
sat::bool_var v = si.add_bool_var(n->get_owner()); sat::bool_var v = si.add_bool_var(e);
s().set_external(v);
sat::literal lit2 = literal(v, false); sat::literal lit2 = literal(v, false);
s().mk_clause(~lit, lit2, sat::status::th(false, m.get_basic_family_id())); s().mk_clause(~lit, lit2, sat::status::asserted());
s().mk_clause(lit, ~lit2, sat::status::th(false, m.get_basic_family_id())); s().mk_clause(lit, ~lit2, sat::status::asserted());
lit = lit2; lit = lit2;
} }
sat::bool_var v = lit.var(); sat::bool_var v = lit.var();
m_var2node.reserve(v + 1, nullptr); m_var2expr.reserve(v + 1, nullptr);
SASSERT(m_var2node[v] == nullptr); SASSERT(m_var2expr[v] == nullptr);
m_var2node[v] = n; m_var2expr[v] = e;
m_var_trail.push_back(v); m_var_trail.push_back(v);
s().set_external(v);
if (!m_egraph.find(e)) {
enode* n = m_egraph.mk(e, 0, nullptr);
m_egraph.set_merge_enabled(n, false);
}
return lit;
} }
bool solver::internalize_root(app* e, bool sign) { bool solver::internalize_root(app* e, bool sign, enode_vector const& args) {
if (m.is_distinct(e)) { if (m.is_distinct(e)) {
enode_vector _args(m_args); enode_vector _args(args);
if (sign) if (sign)
add_not_distinct_axiom(e, _args.c_ptr()); add_not_distinct_axiom(e, _args.c_ptr());
else else
@ -202,7 +220,7 @@ namespace euf {
expr* c = a->get_arg(0); expr* c = a->get_arg(0);
expr* th = a->get_arg(1); expr* th = a->get_arg(1);
expr* el = a->get_arg(2); expr* el = a->get_arg(2);
sat::bool_var v = m_expr2var.to_bool_var(c); sat::bool_var v = si.to_bool_var(c);
SASSERT(v != sat::null_bool_var); SASSERT(v != sat::null_bool_var);
SASSERT(!m.is_bool(e)); SASSERT(!m.is_bool(e));
expr_ref eq_th(m.mk_eq(a, th), m); expr_ref eq_th(m.mk_eq(a, th), m);
@ -224,7 +242,7 @@ namespace euf {
} }
} }
expr_ref fml(m.mk_or(eqs), m); expr_ref fml(m.mk_or(eqs), m);
sat::literal dist(m_expr2var.to_bool_var(e), false); sat::literal dist(si.to_bool_var(e), false);
sat::literal some_eq = si.internalize(fml, m_is_redundant); sat::literal some_eq = si.internalize(fml, m_is_redundant);
sat::literal lits1[2] = { ~dist, ~some_eq }; sat::literal lits1[2] = { ~dist, ~some_eq };
sat::literal lits2[2] = { dist, some_eq }; sat::literal lits2[2] = { dist, some_eq };

View file

@ -0,0 +1,48 @@
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
euf_invariant.cpp
Abstract:
Check invariants for EUF solver.
Author:
Nikolaj Bjorner (nbjorner) 2020-09-07
--*/
#include "sat/smt/euf_solver.h"
namespace euf {
/**
\brief Check if expressions attached to bool_variables and enodes have a consistent assignment.
For all a, b. root(a) == root(b) ==> get_assignment(a) == get_assignment(b)
*/
void solver::check_eqc_bool_assignment() const {
for (enode* n : m_egraph.nodes()) {
VERIFY(!m.is_bool(n->get_owner()) ||
s().value(get_literal(n)) == s().value(get_literal(n->get_root())));
}
}
void solver::check_missing_bool_enode_propagation() const {
for (enode* n : m_egraph.nodes())
if (m.is_bool(n->get_owner()) && l_undef == s().value(get_literal(n))) {
if (!n->is_root()) {
VERIFY(l_undef == s().value(get_literal(n->get_root())));
}
else
for (enode* o : enode_class(n)) {
VERIFY(l_undef == s().value(get_literal(o)));
}
}
}
}

View file

@ -16,6 +16,7 @@ Author:
--*/ --*/
#include "ast/ast_pp.h" #include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "sat/smt/euf_solver.h" #include "sat/smt/euf_solver.h"
#include "model/value_factory.h" #include "model/value_factory.h"
@ -79,7 +80,7 @@ namespace euf {
} }
if (is_app(e) && to_app(e)->get_family_id() == m.get_basic_family_id()) if (is_app(e) && to_app(e)->get_family_id() == m.get_basic_family_id())
continue; continue;
sat::bool_var v = m_expr2var.to_bool_var(e); sat::bool_var v = si.to_bool_var(e);
SASSERT(v != sat::null_bool_var); SASSERT(v != sat::null_bool_var);
switch (s().value(v)) { switch (s().value(v)) {
case l_true: case l_true:
@ -100,6 +101,8 @@ namespace euf {
expr* v = user_sort.get_fresh_value(m.get_sort(e)); expr* v = user_sort.get_fresh_value(m.get_sort(e));
values.set(id, v); values.set(id, v);
} }
else if ((mb = get_solver(m.get_sort(e))))
mb->add_value(n, values);
else { else {
IF_VERBOSE(1, verbose_stream() << "no model values created for " << mk_pp(e, m) << "\n"); IF_VERBOSE(1, verbose_stream() << "no model values created for " << mk_pp(e, m) << "\n");
} }
@ -119,6 +122,9 @@ namespace euf {
if (m.is_bool(e) && is_uninterp_const(e) && mdl->get_const_interp(f)) if (m.is_bool(e) && is_uninterp_const(e) && mdl->get_const_interp(f))
continue; continue;
expr* v = values.get(n->get_root_id()); expr* v = values.get(n->get_root_id());
CTRACE("euf", !v, tout << "no value for " << mk_pp(e, m) << "\n";);
if (!v)
continue;
unsigned arity = f->get_arity(); unsigned arity = f->get_arity();
if (arity == 0) if (arity == 0)
mdl->register_decl(f, v); mdl->register_decl(f, v);
@ -129,8 +135,11 @@ namespace euf {
mdl->register_decl(f, fi); mdl->register_decl(f, fi);
} }
args.reset(); args.reset();
for (enode* arg : enode_args(n)) for (enode* arg : enode_args(n)) {
args.push_back(values.get(arg->get_root_id())); args.push_back(values.get(arg->get_root_id()));
SASSERT(args.back());
}
SASSERT(args.size() == arity);
if (!fi->get_entry(args.c_ptr())) if (!fi->get_entry(args.c_ptr()))
fi->insert_new_entry(args.c_ptr(), v); fi->insert_new_entry(args.c_ptr(), v);
} }

View file

@ -19,53 +19,60 @@ Author:
namespace euf { namespace euf {
void solver::log_node(enode* n) { void solver::init_drat() {
if (m_drat) { if (!m_drat_initialized)
expr* e = n->get_owner(); get_drat().add_theory(m.get_basic_family_id(), symbol("euf"));
if (is_app(e)) { m_drat_initialized = true;
symbol const& name = to_app(e)->get_name();
s().get_drat().def_begin(n->get_owner_id(), name);
for (enode* arg : enode_args(n)) {
s().get_drat().def_add_arg(arg->get_owner_id());
} }
s().get_drat().def_end();
void solver::log_node(expr* e) {
if (!use_drat())
return;
if (is_app(e)) {
std::stringstream strm;
strm << mk_ismt2_func(to_app(e)->get_decl(), m);
get_drat().def_begin(e->get_id(), strm.str());
for (expr* arg : *to_app(e))
get_drat().def_add_arg(arg->get_id());
get_drat().def_end();
} }
else { else {
IF_VERBOSE(0, verbose_stream() << "logging binders is TBD\n"); IF_VERBOSE(0, verbose_stream() << "logging binders is TBD\n");
} }
} }
}
void solver::log_bool_var(sat::bool_var v, enode* n) {
if (m_drat) {
s().get_drat().bool_def(v, n->get_owner_id());
}
}
/**
* \brief logs antecedents to a proof trail.
*
* NB with theories, this is not a pure EUF justification,
* It is true modulo EUF and previously logged certificates
* so it isn't necessarily an axiom over EUF,
* We will here leave it to the EUF checker to perform resolution steps.
*/
void solver::log_antecedents(literal l, literal_vector const& r) { void solver::log_antecedents(literal l, literal_vector const& r) {
TRACE("euf", log_antecedents(tout, l, r);); TRACE("euf", log_antecedents(tout, l, r););
if (m_drat) { if (!use_drat())
return;
literal_vector lits; literal_vector lits;
for (literal lit : r) lits.push_back(~lit); for (literal lit : r) lits.push_back(~lit);
if (l != sat::null_literal) if (l != sat::null_literal)
lits.push_back(l); lits.push_back(l);
s().get_drat().add(lits, sat::status::th(true, m.get_basic_family_id())); get_drat().add(lits, sat::status::th(true, m.get_basic_family_id()));
}
} }
void solver::log_antecedents(std::ostream& out, literal l, literal_vector const& r) { void solver::log_antecedents(std::ostream& out, literal l, literal_vector const& r) {
for (sat::literal l : r) { for (sat::literal l : r) {
enode* n = m_var2node[l.var()]; expr* n = m_var2expr[l.var()];
out << ~l << ": "; out << ~l << ": ";
if (!l.sign()) out << "! "; if (!l.sign()) out << "! ";
out << mk_pp(n->get_owner(), m) << "\n"; out << mk_pp(n, m) << "\n";
SASSERT(s().value(l) == l_true); SASSERT(s().value(l) == l_true);
} }
if (l != sat::null_literal) { if (l != sat::null_literal) {
out << l << ": "; out << l << ": ";
if (l.sign()) out << "! "; if (l.sign()) out << "! ";
enode* n = m_var2node[l.var()]; expr* n = m_var2expr[l.var()];
out << mk_pp(n->get_owner(), m) << "\n"; out << mk_pp(n, m) << "\n";
} }
} }

View file

@ -20,27 +20,25 @@ Author:
#include "sat/sat_solver.h" #include "sat/sat_solver.h"
#include "sat/smt/sat_smt.h" #include "sat/smt/sat_smt.h"
#include "sat/smt/ba_solver.h" #include "sat/smt/ba_solver.h"
#include "sat/smt/bv_solver.h"
#include "sat/smt/euf_solver.h" #include "sat/smt/euf_solver.h"
namespace euf { namespace euf {
void solver::updt_params(params_ref const& p) { void solver::updt_params(params_ref const& p) {
m_config.updt_params(p); m_config.updt_params(p);
m_drat = m_solver && m_solver->get_config().m_drat;
if (m_drat)
m_solver->get_drat().add_theory(m.get_basic_family_id(), symbol("euf"));
} }
/** /**
* retrieve extension that is associated with Boolean variable. * retrieve extension that is associated with Boolean variable.
*/ */
th_solver* solver::get_solver(sat::bool_var v) { th_solver* solver::get_solver(sat::bool_var v) {
if (v >= m_var2node.size()) if (v >= m_var2expr.size())
return nullptr; return nullptr;
euf::enode* n = m_var2node[v]; expr* e = m_var2expr[v];
if (!n) if (!e)
return nullptr; return nullptr;
return get_solver(n->get_owner()); return get_solver(e);
} }
th_solver* solver::get_solver(expr* e) { th_solver* solver::get_solver(expr* e) {
@ -49,8 +47,7 @@ namespace euf {
return nullptr; return nullptr;
} }
th_solver* solver::get_solver(func_decl* f) { th_solver* solver::get_solver(family_id fid, func_decl* f) {
family_id fid = f->get_family_id();
if (fid == null_family_id) if (fid == null_family_id)
return nullptr; return nullptr;
auto* ext = m_id2solver.get(fid, nullptr); auto* ext = m_id2solver.get(fid, nullptr);
@ -59,19 +56,24 @@ namespace euf {
if (fid == m.get_basic_family_id()) if (fid == m.get_basic_family_id())
return nullptr; return nullptr;
pb_util pb(m); pb_util pb(m);
bv_util bvu(m);
if (pb.get_family_id() == fid) { if (pb.get_family_id() == fid) {
ext = alloc(sat::ba_solver, *this, fid); ext = alloc(sat::ba_solver, *this, fid);
if (m_drat) if (use_drat())
m_solver->get_drat().add_theory(fid, symbol("ba")); s().get_drat().add_theory(fid, symbol("ba"));
}
else if (bvu.get_family_id() == fid) {
ext = alloc(bv::solver, *this, fid);
if (use_drat())
s().get_drat().add_theory(fid, symbol("bv"));
} }
if (ext) { if (ext) {
ext->set_solver(m_solver); ext->set_solver(m_solver);
ext->push_scopes(s().num_scopes()); ext->push_scopes(s().num_scopes());
add_solver(fid, ext); add_solver(fid, ext);
} }
else { else if (f)
unhandled_function(f); unhandled_function(f);
}
return ext; return ext;
} }
@ -84,7 +86,7 @@ namespace euf {
if (m_unhandled_functions.contains(f)) if (m_unhandled_functions.contains(f))
return; return;
m_unhandled_functions.push_back(f); m_unhandled_functions.push_back(f);
m_trail.push_back(new (m_region) push_back_vector<solver, func_decl_ref_vector>(m_unhandled_functions)); m_trail.push(push_back_vector<solver, func_decl_ref_vector>(m_unhandled_functions));
IF_VERBOSE(0, verbose_stream() << mk_pp(f, m) << " not handled\n"); IF_VERBOSE(0, verbose_stream() << mk_pp(f, m) << " not handled\n");
} }
@ -93,7 +95,7 @@ namespace euf {
} }
bool solver::is_external(bool_var v) { bool solver::is_external(bool_var v) {
if (nullptr != m_var2node.get(v, nullptr)) if (nullptr != m_var2expr.get(v, nullptr))
return true; return true;
for (auto* s : m_solvers) for (auto* s : m_solvers)
if (s->is_external(v)) if (s->is_external(v))
@ -107,86 +109,125 @@ namespace euf {
return ext->propagate(l, idx); return ext->propagate(l, idx);
} }
void solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector& r) { void solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector& r, bool probing) {
m_egraph.begin_explain();
m_explain.reset();
auto* ext = sat::constraint_base::to_extension(idx); auto* ext = sat::constraint_base::to_extension(idx);
if (ext == this) if (ext == this)
get_antecedents(l, constraint::from_idx(idx), r); get_antecedents(l, constraint::from_idx(idx), r, probing);
else else
ext->get_antecedents(l, idx, r); 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));
else {
size_t idx = get_justification(e);
auto* ext = sat::constraint_base::to_extension(idx);
SASSERT(ext != this);
sat::literal lit = sat::null_literal;
ext->get_antecedents(lit, idx, r, probing);
}
}
m_egraph.end_explain();
if (!probing)
log_antecedents(l, r);
} }
void solver::get_antecedents(literal l, constraint& j, literal_vector& r) { void solver::add_antecedent(enode* a, enode* b) {
m_explain.reset(); m_egraph.explain_eq<size_t>(m_explain, a, b);
}
void solver::propagate(enode* a, enode* b, ext_justification_idx idx) {
m_egraph.merge(a, b, to_ptr(idx));
}
void solver::get_antecedents(literal l, constraint& j, literal_vector& r, bool probing) {
expr* e = nullptr;
euf::enode* n = nullptr; euf::enode* n = nullptr;
// init_ackerman(); init_ackerman();
switch (j.kind()) { switch (j.kind()) {
case constraint::kind_t::conflict: case constraint::kind_t::conflict:
SASSERT(m_egraph.inconsistent()); SASSERT(m_egraph.inconsistent());
m_egraph.explain<unsigned>(m_explain); m_egraph.explain<size_t>(m_explain);
break; break;
case constraint::kind_t::eq: case constraint::kind_t::eq:
n = m_var2node[l.var()]; e = m_var2expr[l.var()];
n = m_egraph.find(e);
SASSERT(n); SASSERT(n);
SASSERT(m_egraph.is_equality(n)); SASSERT(m_egraph.is_equality(n));
SASSERT(!l.sign()); SASSERT(!l.sign());
m_egraph.explain_eq<unsigned>(m_explain, n->get_arg(0), n->get_arg(1)); m_egraph.explain_eq<size_t>(m_explain, n->get_arg(0), n->get_arg(1));
break; break;
case constraint::kind_t::lit: case constraint::kind_t::lit:
n = m_var2node[l.var()]; e = m_var2expr[l.var()];
n = m_egraph.find(e);
SASSERT(n); SASSERT(n);
SASSERT(m.is_bool(n->get_owner())); SASSERT(m.is_bool(n->get_owner()));
m_egraph.explain_eq<unsigned>(m_explain, n, (l.sign() ? mk_false() : mk_true())); m_egraph.explain_eq<size_t>(m_explain, n, (l.sign() ? mk_false() : mk_true()));
break; break;
default: default:
IF_VERBOSE(0, verbose_stream() << (unsigned)j.kind() << "\n"); IF_VERBOSE(0, verbose_stream() << (unsigned)j.kind() << "\n");
UNREACHABLE(); UNREACHABLE();
} }
for (unsigned* idx : m_explain)
r.push_back(sat::to_literal((unsigned)(idx - base_ptr())));
log_antecedents(l, r);
} }
void solver::asserted(literal l) { void solver::asserted(literal l) {
auto* ext = get_solver(l.var()); expr* e = m_var2expr.get(l.var(), nullptr);
if (ext) { if (!e) {
ext->asserted(l);
return; return;
} }
bool sign = l.sign(); bool sign = l.sign();
auto n = m_var2node.get(l.var(), nullptr); TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << " " << (sign ? "not ": " ") << e->get_id() << "\n";);
euf::enode* n = m_egraph.find(e);
if (!n) if (!n)
return; return;
for (auto th : enode_th_vars(n))
expr* e = n->get_owner(); m_id2solver[th.get_id()]->asserted(l);
if (m.is_eq(e) && !sign) { if (!n->merge_enabled())
return;
size_t* c = to_ptr(l);
SASSERT(is_literal(c));
SASSERT(l == get_literal(c));
if (m.is_eq(e) && !sign && n->num_args() == 2) {
euf::enode* na = n->get_arg(0); euf::enode* na = n->get_arg(0);
euf::enode* nb = n->get_arg(1); euf::enode* nb = n->get_arg(1);
TRACE("euf", tout << mk_pp(e, m) << "\n";); m_egraph.merge(na, nb, c);
m_egraph.merge(na, nb, base_ptr() + l.index());
} }
else { else {
euf::enode* nb = sign ? mk_false() : mk_true(); euf::enode* nb = sign ? mk_false() : mk_true();
TRACE("euf", tout << (sign ? "not ": " ") << mk_pp(n->get_owner(), m) << "\n";); m_egraph.merge(n, nb, c);
m_egraph.merge(n, nb, base_ptr() + l.index());
} }
// TBD: delay propagation?
propagate();
} }
void solver::propagate() { bool solver::unit_propagate() {
while (m_egraph.propagate() && !s().inconsistent()) { bool propagated = false;
while (!s().inconsistent()) {
if (m_egraph.inconsistent()) { if (m_egraph.inconsistent()) {
unsigned lvl = s().scope_lvl(); unsigned lvl = s().scope_lvl();
s().set_conflict(sat::justification::mk_ext_justification(lvl, conflict_constraint().to_index())); s().set_conflict(sat::justification::mk_ext_justification(lvl, conflict_constraint().to_index()));
return; return true;
} }
bool propagated1 = false;
if (m_egraph.propagate()) {
propagate_literals(); propagate_literals();
propagate_th_eqs(); propagate_th_eqs();
propagated1 = true;
} }
for (auto* s : m_solvers) {
if (s->unit_propagate())
propagated1 = true;
}
if (!propagated1)
break;
propagated = true;
}
return propagated;
} }
void solver::propagate_literals() { void solver::propagate_literals() {
@ -196,7 +237,7 @@ namespace euf {
bool is_eq = p.second; bool is_eq = p.second;
expr* e = n->get_owner(); expr* e = n->get_owner();
expr* a = nullptr, *b = nullptr; expr* a = nullptr, *b = nullptr;
bool_var v = m_expr2var.to_bool_var(e); bool_var v = si.to_bool_var(e);
SASSERT(m.is_bool(e)); SASSERT(m.is_bool(e));
size_t cnstr; size_t cnstr;
literal lit; literal lit;
@ -207,7 +248,7 @@ namespace euf {
} }
else { else {
a = e, b = n->get_root()->get_owner(); a = e, b = n->get_root()->get_owner();
SASSERT(m.is_true(a) || m.is_false(b)); SASSERT(m.is_true(b) || m.is_false(b));
cnstr = lit_constraint().to_index(); cnstr = lit_constraint().to_index();
lit = literal(v, m.is_false(b)); lit = literal(v, m.is_false(b));
} }
@ -246,6 +287,7 @@ namespace euf {
} }
sat::check_result solver::check() { sat::check_result solver::check() {
TRACE("euf", s().display(tout););
bool give_up = false; bool give_up = false;
bool cont = false; bool cont = false;
for (auto* e : m_solvers) for (auto* e : m_solvers)
@ -262,37 +304,52 @@ namespace euf {
} }
void solver::push() { void solver::push() {
si.push();
scope s; scope s;
s.m_var_lim = m_var_trail.size(); s.m_var_lim = m_var_trail.size();
s.m_trail_lim = m_trail.size();
m_scopes.push_back(s); m_scopes.push_back(s);
m_region.push_scope(); m_trail.push_scope();
for (auto* e : m_solvers) for (auto* e : m_solvers)
e->push(); e->push();
m_egraph.push(); m_egraph.push();
} }
void solver::force_push() {
for (; m_num_scopes > 0; --m_num_scopes) {
}
}
void solver::pop(unsigned n) { void solver::pop(unsigned n) {
m_egraph.pop(n); m_egraph.pop(n);
for (auto* e : m_solvers) for (auto* e : m_solvers)
e->pop(n); e->pop(n);
scope const & s = m_scopes[m_scopes.size() - n]; scope const & s = m_scopes[m_scopes.size() - n];
for (unsigned i = m_var_trail.size(); i-- > s.m_var_lim; ) for (unsigned i = m_var_trail.size(); i-- > s.m_var_lim; )
m_var2node[m_var_trail[i]] = nullptr; m_var2expr[m_var_trail[i]] = nullptr;
m_var_trail.shrink(s.m_var_lim); m_var_trail.shrink(s.m_var_lim);
m_trail.pop_scope(n);
undo_trail_stack(*this, m_trail, s.m_trail_lim);
m_region.pop_scope(n);
m_scopes.shrink(m_scopes.size() - n); m_scopes.shrink(m_scopes.size() - n);
si.pop(n);
}
void solver::start_reinit(unsigned n) {
sat::literal_vector lits;
m_reinit_vars.reset();
m_reinit_exprs.reset();
s().get_reinit_literals(n, lits);
for (sat::literal lit : lits) {
sat::bool_var v = lit.var();
expr* e = bool_var2expr(v);
if (m_reinit_vars.contains(v) || !e)
continue;
m_reinit_vars.push_back(v);
m_reinit_exprs.push_back(e);
}
}
void solver::finish_reinit() {
unsigned sz = m_reinit_vars.size();
for (unsigned i = 0; i < sz; ++i) {
euf::enode* n = get_enode(m_reinit_exprs.get(i));
if (n)
continue;
}
} }
void solver::pre_simplify() { void solver::pre_simplify() {
@ -319,13 +376,33 @@ namespace euf {
return l_undef; return l_undef;
} }
bool solver::set_root(literal l, literal r) {
bool ok = true;
for (auto* s : m_solvers)
if (!s->set_root(l, r))
ok = false;
expr* e = bool_var2expr(l.var());
if (e) {
if (m.is_eq(e) && !m.is_iff(e))
ok = false;
euf::enode* n = get_enode(e);
if (n && n->merge_enabled())
ok = false;
}
return ok;
}
void solver::flush_roots() {
for (auto* s : m_solvers)
s->flush_roots();
}
std::ostream& solver::display(std::ostream& out) const { std::ostream& solver::display(std::ostream& out) const {
m_egraph.display(out); m_egraph.display(out);
out << "bool-vars\n"; out << "bool-vars\n";
for (unsigned v : m_var_trail) { for (unsigned v : m_var_trail) {
euf::enode* n = m_var2node[v]; expr* e = m_var2expr[v];
out << v << ": " << n->get_owner_id() << " " << mk_bounded_pp(n->get_owner(), m, 1) << "\n"; out << v << ": " << e->get_id() << " " << m_solver->value(v) << " " << mk_bounded_pp(e, m, 1) << "\n";
} }
for (auto* e : m_solvers) for (auto* e : m_solvers)
e->display(out); e->display(out);
@ -350,13 +427,19 @@ namespace euf {
m_egraph.collect_statistics(st); m_egraph.collect_statistics(st);
for (auto* e : m_solvers) for (auto* e : m_solvers)
e->collect_statistics(st); e->collect_statistics(st);
st.update("euf dynack", m_stats.m_num_dynack); st.update("euf ackerman", m_stats.m_ackerman);
} }
sat::extension* solver::copy(sat::solver* s) { sat::extension* solver::copy(sat::solver* s) {
auto* r = alloc(solver, *m_to_m, *m_to_expr2var, *m_to_si); auto* r = alloc(solver, *m_to_m, *m_to_si);
r->m_config = m_config; r->m_config = m_config;
std::function<void* (void*)> copy_justification = [&](void* x) { return (void*)(r->base_ptr() + ((unsigned*)x - base_ptr())); }; sat::literal true_lit = sat::null_literal;
if (s->init_trail_size() > 0)
true_lit = s->trail_literal(0);
std::function<void* (void*)> copy_justification = [&](void* x) {
SASSERT(true_lit != sat::null_literal);
return (void*)(r->to_ptr(true_lit));
};
r->m_egraph.copy_from(m_egraph, copy_justification); r->m_egraph.copy_from(m_egraph, copy_justification);
r->set_solver(s); r->set_solver(s);
for (unsigned i = 0; i < m_id2solver.size(); ++i) { for (unsigned i = 0; i < m_id2solver.size(); ++i) {
@ -386,6 +469,9 @@ namespace euf {
for (auto* e : m_solvers) for (auto* e : m_solvers)
if (!e->validate()) if (!e->validate())
return false; return false;
check_eqc_bool_assignment();
check_missing_bool_enode_propagation();
m_egraph.invariant();
return true; return true;
} }
@ -411,9 +497,9 @@ namespace euf {
unsigned solver::max_var(unsigned w) const { unsigned solver::max_var(unsigned w) const {
for (auto* e : m_solvers) for (auto* e : m_solvers)
w = e->max_var(w); w = e->max_var(w);
for (unsigned sz = m_var2node.size(); sz-- > 0; ) { for (unsigned sz = m_var2expr.size(); sz-- > 0; ) {
euf::enode* n = m_var2node[sz]; expr* n = m_var2expr[sz];
if (n && m.is_bool(n->get_owner())) { if (n && m.is_bool(n)) {
w = std::max(w, sz); w = std::max(w, sz);
break; break;
} }
@ -422,21 +508,15 @@ namespace euf {
} }
double solver::get_reward(literal l, ext_constraint_idx idx, sat::literal_occs_fun& occs) const { double solver::get_reward(literal l, ext_constraint_idx idx, sat::literal_occs_fun& occs) const {
double r = 0; auto* ext = sat::constraint_base::to_extension(idx);
for (auto* e : m_solvers) { SASSERT(ext);
r = e->get_reward(l, idx, occs); return (ext == this) ? 0 : ext->get_reward(l, idx, occs);
if (r != 0)
return r;
}
return r;
} }
bool solver::is_extended_binary(ext_justification_idx idx, literal_vector& r) { bool solver::is_extended_binary(ext_justification_idx idx, literal_vector& r) {
for (auto* e : m_solvers) { auto* ext = sat::constraint_base::to_extension(idx);
if (e->is_extended_binary(idx, r)) SASSERT(ext);
return true; return (ext != this) && ext->is_extended_binary(idx, r);
}
return false;
} }
void solver::init_ackerman() { void solver::init_ackerman() {

View file

@ -48,41 +48,52 @@ namespace euf {
size_t to_index() const { return sat::constraint_base::mem2base(this); } size_t to_index() const { return sat::constraint_base::mem2base(this); }
}; };
class solver : public sat::extension, public th_internalizer, public th_decompile { class solver : public sat::extension, public th_internalizer, public th_decompile {
typedef top_sort<euf::enode> deps_t; typedef top_sort<euf::enode> deps_t;
friend class ackerman; friend class ackerman;
// friend class sat::ba_solver; // friend class sat::ba_solver;
struct stats { struct stats {
unsigned m_num_dynack; unsigned m_ackerman;
stats() { reset(); } stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); } void reset() { memset(this, 0, sizeof(*this)); }
}; };
struct scope { struct scope {
unsigned m_var_lim; unsigned m_var_lim;
unsigned m_trail_lim;
}; };
typedef ptr_vector<trail<solver> > trail_stack; typedef trail_stack<solver> euf_trail_stack;
size_t* to_ptr(sat::literal l) { return TAG(size_t*, reinterpret_cast<size_t*>((size_t)(l.index() << 4)), 1); }
size_t* to_ptr(size_t jst) { return TAG(size_t*, reinterpret_cast<size_t*>(jst), 2); }
bool is_literal(size_t* p) const { return GET_TAG(p) == 1; }
bool is_justification(size_t* p) const { return GET_TAG(p) == 2; }
sat::literal get_literal(size_t* p) {
unsigned idx = static_cast<unsigned>(reinterpret_cast<size_t>(UNTAG(size_t*, p)));
return sat::to_literal(idx >> 4);
}
size_t get_justification(size_t* p) const {
return reinterpret_cast<size_t>(UNTAG(size_t*, p));
}
ast_manager& m; ast_manager& m;
atom2bool_var& m_expr2var;
sat::sat_internalizer& si; sat::sat_internalizer& si;
smt_params m_config; smt_params m_config;
bool m_drat { false };
euf::egraph m_egraph; euf::egraph m_egraph;
euf_trail_stack m_trail;
stats m_stats; stats m_stats;
region m_region;
func_decl_ref_vector m_unhandled_functions; func_decl_ref_vector m_unhandled_functions;
trail_stack m_trail;
sat::solver* m_solver { nullptr }; sat::solver* m_solver { nullptr };
sat::lookahead* m_lookahead { nullptr }; sat::lookahead* m_lookahead { nullptr };
ast_manager* m_to_m; ast_manager* m_to_m;
atom2bool_var* m_to_expr2var;
sat::sat_internalizer* m_to_si; sat::sat_internalizer* m_to_si;
scoped_ptr<euf::ackerman> m_ackerman; scoped_ptr<euf::ackerman> m_ackerman;
ptr_vector<euf::enode> m_var2node; ptr_vector<expr> m_var2expr;
ptr_vector<unsigned> m_explain; ptr_vector<size_t> m_explain;
unsigned m_num_scopes { 0 }; unsigned m_num_scopes { 0 };
unsigned_vector m_var_trail; unsigned_vector m_var_trail;
svector<scope> m_scopes; svector<scope> m_scopes;
@ -93,26 +104,29 @@ namespace euf {
constraint* m_eq { nullptr }; constraint* m_eq { nullptr };
constraint* m_lit { nullptr }; constraint* m_lit { nullptr };
sat::solver& s() { return *m_solver; }
unsigned * base_ptr() { return reinterpret_cast<unsigned*>(this); }
// internalization // internalization
bool visit(expr* e) override; bool visit(expr* e) override;
bool visited(expr* e) override; bool visited(expr* e) override;
bool post_visit(expr* e, bool sign, bool root) override; bool post_visit(expr* e, bool sign, bool root) override;
void attach_node(euf::enode* n); sat::literal attach_lit(sat::literal lit, expr* e);
void attach_lit(sat::literal lit, euf::enode* n);
void add_distinct_axiom(app* e, euf::enode* const* args); void add_distinct_axiom(app* e, euf::enode* const* args);
void add_not_distinct_axiom(app* e, euf::enode* const* args); void add_not_distinct_axiom(app* e, euf::enode* const* args);
void axiomatize_basic(enode* n); void axiomatize_basic(enode* n);
bool internalize_root(app* e, bool sign); bool internalize_root(app* e, bool sign, ptr_vector<enode> const& args);
euf::enode* mk_true(); euf::enode* mk_true();
euf::enode* mk_false(); euf::enode* mk_false();
// replay
expr_ref_vector m_reinit_exprs;
sat::bool_var_vector m_reinit_vars;
void start_reinit(unsigned num_scopes);
void finish_reinit();
// extensions // extensions
th_solver* get_solver(func_decl* f); th_solver* get_solver(family_id fid, func_decl* f);
th_solver* get_solver(sort* s) { return get_solver(s->get_family_id(), nullptr); }
th_solver* get_solver(func_decl* f) { return get_solver(f->get_family_id(), f); }
th_solver* get_solver(expr* e); th_solver* get_solver(expr* e);
th_solver* get_solver(sat::bool_var v); th_solver* get_solver(sat::bool_var v);
void add_solver(family_id fid, th_solver* th); void add_solver(family_id fid, th_solver* th);
@ -127,15 +141,20 @@ namespace euf {
void values2model(deps_t const& deps, expr_ref_vector const& values, model_ref& mdl); void values2model(deps_t const& deps, expr_ref_vector const& values, model_ref& mdl);
// solving // solving
void propagate();
void propagate_literals(); void propagate_literals();
void propagate_th_eqs(); void propagate_th_eqs();
void get_antecedents(literal l, constraint& j, literal_vector& r); void get_antecedents(literal l, constraint& j, literal_vector& r, bool probing);
void force_push();
// proofs
void log_antecedents(std::ostream& out, literal l, literal_vector const& r); void log_antecedents(std::ostream& out, literal l, literal_vector const& r);
void log_antecedents(literal l, literal_vector const& r); void log_antecedents(literal l, literal_vector const& r);
void log_node(enode* n); void log_node(expr* n);
void log_bool_var(sat::bool_var v, enode* n); bool m_drat_initialized{ false };
void init_drat();
// invariant
void check_eqc_bool_assignment() const;
void check_missing_bool_enode_propagation() const;
constraint& mk_constraint(constraint*& c, constraint::kind_t k); constraint& mk_constraint(constraint*& c, constraint::kind_t k);
constraint& conflict_constraint() { return mk_constraint(m_conflict, constraint::kind_t::conflict); } constraint& conflict_constraint() { return mk_constraint(m_conflict, constraint::kind_t::conflict); }
@ -143,17 +162,17 @@ namespace euf {
constraint& lit_constraint() { return mk_constraint(m_lit, constraint::kind_t::lit); } constraint& lit_constraint() { return mk_constraint(m_lit, constraint::kind_t::lit); }
public: public:
solver(ast_manager& m, atom2bool_var& expr2var, sat::sat_internalizer& si, params_ref const& p = params_ref()): solver(ast_manager& m, sat::sat_internalizer& si, params_ref const& p = params_ref()):
m(m), m(m),
m_expr2var(expr2var),
si(si), si(si),
m_egraph(m), m_egraph(m),
m_trail(*this),
m_unhandled_functions(m), m_unhandled_functions(m),
m_solver(nullptr), m_solver(nullptr),
m_lookahead(nullptr), m_lookahead(nullptr),
m_to_m(&m), m_to_m(&m),
m_to_expr2var(&expr2var), m_to_si(&si),
m_to_si(&si) m_reinit_exprs(m)
{ {
updt_params(p); updt_params(p);
} }
@ -166,37 +185,45 @@ namespace euf {
struct scoped_set_translate { struct scoped_set_translate {
solver& s; solver& s;
scoped_set_translate(solver& s, ast_manager& m, atom2bool_var& a2b, sat::sat_internalizer& si) : scoped_set_translate(solver& s, ast_manager& m, sat::sat_internalizer& si) :
s(s) { s(s) {
s.m_to_m = &m; s.m_to_m = &m;
s.m_to_expr2var = &a2b;
s.m_to_si = &si; s.m_to_si = &si;
} }
~scoped_set_translate() { ~scoped_set_translate() {
s.m_to_m = &s.m; s.m_to_m = &s.m;
s.m_to_expr2var = &s.m_expr2var;
s.m_to_si = &s.si; s.m_to_si = &s.si;
} }
}; };
sat::solver& s() { return *m_solver; }
sat::solver const& s() const { return *m_solver; }
sat::sat_internalizer& get_si() { return si; } sat::sat_internalizer& get_si() { return si; }
ast_manager& get_manager() { return m; } ast_manager& get_manager() { return m; }
enode* get_enode(expr* e) { return m_egraph.find(e); } enode* get_enode(expr* e) { return m_egraph.find(e); }
sat::literal get_literal(expr* e) { return literal(m_expr2var.to_bool_var(e), false); } 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_owner()); }
smt_params const& get_config() { return m_config; } smt_params const& get_config() { return m_config; }
region& get_region() { return m_region; } region& get_region() { return m_trail.get_region(); }
template <typename C> template <typename C>
void push(C const& c) { m_trail.push_back(new (m_region) C(c)); } void push(C const& c) { m_trail.push(c); }
euf_trail_stack& get_trail_stack() { return m_trail; }
void updt_params(params_ref const& p); void updt_params(params_ref const& p);
void set_solver(sat::solver* s) override { m_solver = s; m_drat = s->get_config().m_drat; } void set_solver(sat::solver* s) override { m_solver = s; }
void set_lookahead(sat::lookahead* s) override { m_lookahead = s; } void set_lookahead(sat::lookahead* s) override { m_lookahead = s; }
void init_search() override; void init_search() override;
double get_reward(literal l, ext_constraint_idx idx, sat::literal_occs_fun& occs) const override; double get_reward(literal l, ext_constraint_idx idx, sat::literal_occs_fun& occs) const override;
bool is_extended_binary(ext_justification_idx idx, literal_vector& r) override; bool is_extended_binary(ext_justification_idx idx, literal_vector& r) override;
bool is_external(bool_var v) override; bool is_external(bool_var v) override;
bool propagate(literal l, ext_constraint_idx idx) override; bool propagate(literal l, ext_constraint_idx idx) override;
void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) override; bool unit_propagate() override;
void propagate(enode* a, enode* b, ext_justification_idx);
bool set_root(literal l, literal r) override;
void flush_roots() override;
void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r, bool probing) override;
void add_antecedent(enode* a, enode* b);
void asserted(literal l) override; void asserted(literal l) override;
sat::check_result check() override; sat::check_result check() override;
void push() override; void push() override;
@ -220,6 +247,9 @@ namespace euf {
bool check_model(sat::model const& m) const override; bool check_model(sat::model const& m) const override;
unsigned max_var(unsigned w) const override; unsigned max_var(unsigned w) const override;
bool use_drat() { return s().get_config().m_drat && (init_drat(), true); }
sat::drat& get_drat() { return s().get_drat(); }
// decompile // decompile
bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card, bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card,
std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) override; std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) override;
@ -228,11 +258,18 @@ namespace euf {
// internalize // internalize
sat::literal internalize(expr* e, bool sign, bool root, bool learned) override; sat::literal internalize(expr* e, bool sign, bool root, bool learned) override;
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_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);
euf::enode* mk_enode(expr* e, unsigned n, enode* const* args) { return m_egraph.mk(e, n, args); } 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 update_model(model_ref& mdl); void update_model(model_ref& mdl);
func_decl_ref_vector const& unhandled_functions() { return m_unhandled_functions; } func_decl_ref_vector const& unhandled_functions() { return m_unhandled_functions; }
}; };
}; };
inline std::ostream& operator<<(std::ostream& out, euf::solver const& s) {
return s.display(out);
}

View file

@ -39,8 +39,11 @@ namespace sat {
virtual ~sat_internalizer() {} virtual ~sat_internalizer() {}
virtual bool is_bool_op(expr* e) const = 0; virtual bool is_bool_op(expr* e) const = 0;
virtual literal internalize(expr* e, bool learned) = 0; virtual literal internalize(expr* e, bool learned) = 0;
virtual bool_var to_bool_var(expr* e) = 0;
virtual bool_var add_bool_var(expr* e) = 0; virtual bool_var add_bool_var(expr* e) = 0;
virtual void cache(app* t, literal l) = 0; virtual void cache(app* t, literal l) = 0;
virtual void push() = 0;
virtual void pop(unsigned n) = 0;
}; };
class constraint_base { class constraint_base {

View file

@ -65,15 +65,24 @@ namespace euf {
return ctx.get_region(); return ctx.get_region();
} }
enode* th_euf_solver::get_enode(expr* e) const { trail_stack<euf::solver>& th_euf_solver::get_trail_stack() {
return ctx.get_trail_stack();
}
enode* th_euf_solver::expr2enode(expr* e) const {
return ctx.get_enode(e); return ctx.get_enode(e);
} }
sat::literal th_euf_solver::get_literal(expr* e) const { sat::literal th_euf_solver::expr2literal(expr* e) const {
return ctx.get_literal(e); return ctx.get_literal(e);
} }
expr* th_euf_solver::bool_var2expr(sat::bool_var v) const {
return ctx.bool_var2expr(v);
}
theory_var th_euf_solver::mk_var(enode * n) { theory_var th_euf_solver::mk_var(enode * n) {
force_push();
SASSERT(!is_attached_to_var(n)); SASSERT(!is_attached_to_var(n));
euf::theory_var v = m_var2enode.size(); euf::theory_var v = m_var2enode.size();
m_var2enode.push_back(n); m_var2enode.push_back(n);
@ -82,7 +91,7 @@ namespace euf {
bool th_euf_solver::is_attached_to_var(enode* n) const { bool th_euf_solver::is_attached_to_var(enode* n) const {
theory_var v = n->get_th_var(get_id()); theory_var v = n->get_th_var(get_id());
return v != null_theory_var && get_enode(v) == n; return v != null_theory_var && var2enode(v) == n;
} }
theory_var th_euf_solver::get_th_var(expr* e) const { theory_var th_euf_solver::get_th_var(expr* e) const {
@ -99,4 +108,35 @@ namespace euf {
m_var2enode_lim.shrink(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::add_unit(sat::literal lit) {
ctx.s().add_clause(1, &lit, sat::status::th(m_is_redundant, get_id()));
}
void th_euf_solver::add_clause(sat::literal a, sat::literal b) {
sat::literal lits[2] = { a, b };
ctx.s().add_clause(2, lits, sat::status::th(m_is_redundant, get_id()));
}
void th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c) {
sat::literal lits[3] = { a, b, c };
ctx.s().add_clause(3, lits, sat::status::th(m_is_redundant, get_id()));
}
void th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d) {
sat::literal lits[4] = { a, b, c, d };
ctx.s().add_clause(4, lits, sat::status::th(m_is_redundant, get_id()));
}
} }

View file

@ -36,11 +36,14 @@ namespace euf {
virtual bool visit(expr* e) { return false; } virtual bool visit(expr* e) { return false; }
virtual bool visited(expr* e) { return false; } virtual bool visited(expr* e) { return false; }
virtual bool post_visit(expr* e, bool sign, bool root) { return false; } virtual bool post_visit(expr* e, bool sign, bool root) { return false; }
public: public:
virtual ~th_internalizer() {} virtual ~th_internalizer() {}
virtual sat::literal internalize(expr* e, bool sign, bool root, bool redundant) = 0; virtual sat::literal internalize(expr* e, bool sign, bool root, bool redundant) = 0;
virtual void internalize(expr* e, bool redundant) = 0;
/** /**
\brief Apply (interpreted) sort constraints on the given enode. \brief Apply (interpreted) sort constraints on the given enode.
*/ */
@ -69,7 +72,7 @@ namespace euf {
/** /**
\brief compute dependencies for node n \brief compute dependencies for node n
*/ */
virtual void add_dep(euf::enode* n, top_sort<euf::enode>& dep) {} virtual void add_dep(euf::enode* n, top_sort<euf::enode>& dep) { dep.insert(n, nullptr); }
/** /**
\brief should function be included in model. \brief should function be included in model.
@ -102,24 +105,39 @@ namespace euf {
solver & ctx; solver & ctx;
euf::enode_vector m_var2enode; euf::enode_vector m_var2enode;
unsigned_vector m_var2enode_lim; unsigned_vector m_var2enode_lim;
unsigned m_num_scopes{ 0 };
smt_params const& get_config() const; smt_params const& get_config() const;
sat::literal get_literal(expr* e) const; sat::literal expr2literal(expr* e) const;
region& get_region(); region& get_region();
void add_unit(sat::literal lit);
void add_clause(sat::literal a, sat::literal b);
void add_clause(sat::literal a, sat::literal b, sat::literal c);
void add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d);
public: public:
th_euf_solver(euf::solver& ctx, euf::theory_id id); th_euf_solver(euf::solver& ctx, euf::theory_id id);
virtual ~th_euf_solver() {} virtual ~th_euf_solver() {}
virtual theory_var mk_var(enode * n); virtual theory_var mk_var(enode * n);
unsigned get_num_vars() const { return m_var2enode.size();} unsigned get_num_vars() const { return m_var2enode.size();}
enode* get_enode(expr* e) const; enode* expr2enode(expr* e) const;
enode* get_enode(theory_var v) const { return m_var2enode[v]; } enode* var2enode(theory_var v) const { return m_var2enode[v]; }
expr* get_expr(theory_var v) const { return get_enode(v)->get_owner(); } expr* var2expr(theory_var v) const { return var2enode(v)->get_owner(); }
expr_ref get_expr(sat::literal lit) const { expr* e = get_expr(lit.var()); return lit.sign() ? expr_ref(m.mk_not(e), m) : expr_ref(e, m); } expr* bool_var2expr(sat::bool_var v) const;
expr_ref literal2expr(sat::literal lit) const { expr* e = bool_var2expr(lit.var()); return lit.sign() ? expr_ref(m.mk_not(e), m) : expr_ref(e, m); }
theory_var get_th_var(enode* n) const { return n->get_th_var(get_id()); } theory_var get_th_var(enode* n) const { return n->get_th_var(get_id()); }
theory_var get_th_var(expr* e) const; theory_var get_th_var(expr* e) const;
trail_stack<euf::solver> & get_trail_stack();
bool is_attached_to_var(enode* n) const; bool is_attached_to_var(enode* n) const;
bool is_root(theory_var v) const { return var2enode(v)->is_root(); }
void push() override; void push() override;
void pop(unsigned n) override; 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

@ -29,6 +29,7 @@ Notes:
#include "util/ref_util.h" #include "util/ref_util.h"
#include "ast/ast_smt2_pp.h" #include "ast/ast_smt2_pp.h"
#include "ast/ast_pp.h" #include "ast/ast_pp.h"
#include "ast/ast_smt2_pp.h"
#include "ast/ast_ll_pp.h" #include "ast/ast_ll_pp.h"
#include "ast/pb_decl_plugin.h" #include "ast/pb_decl_plugin.h"
#include "ast/ast_util.h" #include "ast/ast_util.h"
@ -38,6 +39,7 @@ Notes:
#include "tactic/tactic.h" #include "tactic/tactic.h"
#include "tactic/generic_model_converter.h" #include "tactic/generic_model_converter.h"
#include "sat/sat_cut_simplifier.h" #include "sat/sat_cut_simplifier.h"
#include "sat/sat_drat.h"
#include "sat/tactic/goal2sat.h" #include "sat/tactic/goal2sat.h"
#include "sat/smt/ba_solver.h" #include "sat/smt/ba_solver.h"
#include "sat/smt/euf_solver.h" #include "sat/smt/euf_solver.h"
@ -72,6 +74,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
bool m_default_external; bool m_default_external;
bool m_xor_solver; bool m_xor_solver;
bool m_euf; bool m_euf;
bool m_drat;
bool m_is_redundant { false }; bool m_is_redundant { false };
sat::literal_vector aig_lits; sat::literal_vector aig_lits;
@ -98,6 +101,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX));
m_xor_solver = p.get_bool("xor_solver", false); m_xor_solver = p.get_bool("xor_solver", false);
m_euf = sp.euf(); m_euf = sp.euf();
m_drat = sp.drat_file() != symbol();
} }
void throw_op_not_handled(std::string const& s) { void throw_op_not_handled(std::string const& s) {
@ -125,19 +129,71 @@ struct goal2sat::imp : public sat::sat_internalizer {
m_solver.add_clause(num, lits, m_is_redundant ? sat::status::redundant() : sat::status::asserted()); m_solver.add_clause(num, lits, m_is_redundant ? sat::status::redundant() : sat::status::asserted());
} }
void mk_root_clause(sat::literal l) {
TRACE("goal2sat", tout << "mk_clause: " << l << "\n";);
m_solver.add_clause(1, &l, m_is_redundant ? sat::status::redundant() : sat::status::input());
}
void mk_root_clause(sat::literal l1, sat::literal l2) {
TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << "\n";);
m_solver.add_clause(l1, l2, m_is_redundant ? sat::status::redundant() : sat::status::input());
}
void mk_root_clause(sat::literal l1, sat::literal l2, sat::literal l3) {
TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << " " << l3 << "\n";);
m_solver.add_clause(l1, l2, l3, m_is_redundant ? sat::status::redundant() : sat::status::input());
}
void mk_root_clause(unsigned num, sat::literal * lits) {
TRACE("goal2sat", tout << "mk_clause: "; for (unsigned i = 0; i < num; i++) tout << lits[i] << " "; tout << "\n";);
m_solver.add_clause(num, lits, m_is_redundant ? sat::status::redundant() : sat::status::input());
}
sat::bool_var add_var(bool is_ext, expr* n) {
auto v = m_solver.add_var(is_ext);
log_node(v, n);
return v;
}
void log_node(sat::bool_var v, expr* n) {
if (m_drat && m_solver.get_drat_ptr()) {
sat::drat* drat = m_solver.get_drat_ptr();
if (is_app(n)) {
app* a = to_app(n);
std::stringstream strm;
strm << mk_ismt2_func(a->get_decl(), m);
drat->def_begin(n->get_id(), strm.str());
for (expr* arg : *a)
drat->def_add_arg(arg->get_id());
drat->def_end();
}
else {
IF_VERBOSE(0, verbose_stream() << "skipping DRAT of non-app\n");
}
drat->bool_def(v, n->get_id());
}
}
sat::literal mk_true() { sat::literal mk_true() {
if (m_true == sat::null_literal) { if (m_true == sat::null_literal) {
// create fake variable to represent true; // create fake variable to represent true;
m_true = sat::literal(m_solver.add_var(false), false); m_true = sat::literal(add_var(false, m.mk_true()), false);
mk_clause(m_true); // v is true mk_clause(m_true); // v is true
} }
return m_true; return m_true;
} }
sat::bool_var to_bool_var(expr* e) override {
return m_map.to_bool_var(e);
}
sat::bool_var add_bool_var(expr* t) override { sat::bool_var add_bool_var(expr* t) override {
sat::bool_var v = m_map.to_bool_var(t); sat::bool_var v = m_map.to_bool_var(t);
if (v == sat::null_bool_var) { if (v == sat::null_bool_var) {
v = m_solver.add_var(true); v = add_var(true, t);
force_push();
m_map.insert(t, v); m_map.insert(t, v);
} }
else { else {
@ -146,6 +202,28 @@ struct goal2sat::imp : public sat::sat_internalizer {
return v; return v;
} }
unsigned m_num_scopes{ 0 };
void force_push() {
for (; m_num_scopes > 0; --m_num_scopes)
m_map.push();
}
void push() override {
++m_num_scopes;
}
void pop(unsigned n) override {
if (n <= m_num_scopes) {
m_num_scopes -= n;
return;
}
n -= m_num_scopes;
m_num_scopes = 0;
m_cache.reset();
m_map.pop(n);
}
void cache(app* t, sat::literal l) override { void cache(app* t, sat::literal l) override {
m_cache.insert(t, l); m_cache.insert(t, l);
} }
@ -167,11 +245,6 @@ struct goal2sat::imp : public sat::sat_internalizer {
throw_op_not_handled(strm.str()); throw_op_not_handled(strm.str());
} }
else { else {
bool ext = m_default_external || !is_uninterp_const(t) || m_interface_vars.contains(t);
v = m_solver.add_var(ext);
m_map.insert(t, v);
l = sat::literal(v, sign);
TRACE("sat", tout << "new_var: " << v << ": " << mk_bounded_pp(t, m, 2) << " " << is_uninterp_const(t) << "\n";);
if (!is_uninterp_const(t)) { if (!is_uninterp_const(t)) {
if (m_euf) { if (m_euf) {
convert_euf(t, root, sign); convert_euf(t, root, sign);
@ -180,6 +253,12 @@ struct goal2sat::imp : public sat::sat_internalizer {
else else
m_unhandled_funs.push_back(to_app(t)->get_decl()); m_unhandled_funs.push_back(to_app(t)->get_decl());
} }
bool ext = m_default_external || !is_uninterp_const(t) || m_interface_vars.contains(t);
v = add_var(ext, t);
force_push();
m_map.insert(t, v);
l = sat::literal(v, sign);
TRACE("sat", tout << "new_var: " << v << ": " << mk_bounded_pp(t, m, 2) << " " << is_uninterp_const(t) << "\n";);
} }
} }
else { else {
@ -191,7 +270,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
m_result_stack.reset(); m_result_stack.reset();
SASSERT(l != sat::null_literal); SASSERT(l != sat::null_literal);
if (root) if (root)
mk_clause(l); mk_root_clause(l);
else else
m_result_stack.push_back(l); m_result_stack.push_back(l);
} }
@ -213,7 +292,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (sign) if (sign)
l.neg(); l.neg();
if (root) if (root)
mk_clause(l); mk_root_clause(l);
else else
m_result_stack.push_back(l); m_result_stack.push_back(l);
return true; return true;
@ -275,17 +354,17 @@ struct goal2sat::imp : public sat::sat_internalizer {
for (unsigned i = 0; i < num; i++) { for (unsigned i = 0; i < num; i++) {
sat::literal l = m_result_stack[i]; sat::literal l = m_result_stack[i];
l.neg(); l.neg();
mk_clause(l); mk_root_clause(l);
} }
} }
else { else {
mk_clause(m_result_stack.size(), m_result_stack.c_ptr()); mk_root_clause(m_result_stack.size(), m_result_stack.c_ptr());
} }
m_result_stack.reset(); m_result_stack.reset();
} }
else { else {
SASSERT(num <= m_result_stack.size()); SASSERT(num <= m_result_stack.size());
sat::bool_var k = m_solver.add_var(false); sat::bool_var k = add_var(false, t);
sat::literal l(k, false); sat::literal l(k, false);
m_cache.insert(t, l); m_cache.insert(t, l);
sat::literal * lits = m_result_stack.end() - num; sat::literal * lits = m_result_stack.end() - num;
@ -321,18 +400,18 @@ struct goal2sat::imp : public sat::sat_internalizer {
for (unsigned i = 0; i < num; ++i) { for (unsigned i = 0; i < num; ++i) {
m_result_stack[i].neg(); m_result_stack[i].neg();
} }
mk_clause(m_result_stack.size(), m_result_stack.c_ptr()); mk_root_clause(m_result_stack.size(), m_result_stack.c_ptr());
} }
else { else {
for (unsigned i = 0; i < num; ++i) { for (unsigned i = 0; i < num; ++i) {
mk_clause(m_result_stack[i]); mk_root_clause(m_result_stack[i]);
} }
} }
m_result_stack.reset(); m_result_stack.reset();
} }
else { else {
SASSERT(num <= m_result_stack.size()); SASSERT(num <= m_result_stack.size());
sat::bool_var k = m_solver.add_var(false); sat::bool_var k = add_var(false, t);
sat::literal l(k, false); sat::literal l(k, false);
m_cache.insert(t, l); m_cache.insert(t, l);
sat::literal * lits = m_result_stack.end() - num; sat::literal * lits = m_result_stack.end() - num;
@ -373,17 +452,17 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (root) { if (root) {
SASSERT(sz == 3); SASSERT(sz == 3);
if (sign) { if (sign) {
mk_clause(~c, ~t); mk_root_clause(~c, ~t);
mk_clause(c, ~e); mk_root_clause(c, ~e);
} }
else { else {
mk_clause(~c, t); mk_root_clause(~c, t);
mk_clause(c, e); mk_root_clause(c, e);
} }
m_result_stack.reset(); m_result_stack.reset();
} }
else { else {
sat::bool_var k = m_solver.add_var(false); sat::bool_var k = add_var(false, n);
sat::literal l(k, false); sat::literal l(k, false);
m_cache.insert(n, l); m_cache.insert(n, l);
mk_clause(~l, ~c, t); mk_clause(~l, ~c, t);
@ -411,16 +490,16 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (root) { if (root) {
SASSERT(sz == 2); SASSERT(sz == 2);
if (sign) { if (sign) {
mk_clause(l1); mk_root_clause(l1);
mk_clause(~l2); mk_root_clause(~l2);
} }
else { else {
mk_clause(~l1, l2); mk_root_clause(~l1, l2);
} }
m_result_stack.reset(); m_result_stack.reset();
} }
else { else {
sat::bool_var k = m_solver.add_var(false); sat::bool_var k = add_var(false, t);
sat::literal l(k, false); sat::literal l(k, false);
m_cache.insert(t, l); m_cache.insert(t, l);
// l <=> (l1 => l2) // l <=> (l1 => l2)
@ -443,17 +522,17 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (root) { if (root) {
SASSERT(sz == 2); SASSERT(sz == 2);
if (sign) { if (sign) {
mk_clause(l1, l2); mk_root_clause(l1, l2);
mk_clause(~l1, ~l2); mk_root_clause(~l1, ~l2);
} }
else { else {
mk_clause(l1, ~l2); mk_root_clause(l1, ~l2);
mk_clause(~l1, l2); mk_root_clause(~l1, l2);
} }
m_result_stack.reset(); m_result_stack.reset();
} }
else { else {
sat::bool_var k = m_solver.add_var(false); sat::bool_var k = add_var(false, t);
sat::literal l(k, false); sat::literal l(k, false);
m_cache.insert(t, l); m_cache.insert(t, l);
mk_clause(~l, l1, ~l2); mk_clause(~l, l1, ~l2);
@ -486,7 +565,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
sat::extension* ext = m_solver.get_extension(); sat::extension* ext = m_solver.get_extension();
euf::solver* euf = nullptr; euf::solver* euf = nullptr;
if (!ext) { if (!ext) {
euf = alloc(euf::solver, m, m_map, *this); euf = alloc(euf::solver, m, *this);
m_solver.set_extension(euf); m_solver.set_extension(euf);
for (unsigned i = m_solver.num_scopes(); i-- > 0; ) for (unsigned i = m_solver.num_scopes(); i-- > 0; )
euf->push(); euf->push();
@ -502,7 +581,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (lit == sat::null_literal) if (lit == sat::null_literal)
return; return;
if (root) if (root)
mk_clause(lit); mk_root_clause(lit);
else else
m_result_stack.push_back(lit); m_result_stack.push_back(lit);
} }
@ -528,7 +607,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (lit == sat::null_literal) if (lit == sat::null_literal)
return; return;
if (root) if (root)
mk_clause(lit); mk_root_clause(lit);
else else
m_result_stack.push_back(lit); m_result_stack.push_back(lit);
} }
@ -643,6 +722,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
sat::literal internalize(expr* n, bool redundant) override { sat::literal internalize(expr* n, bool redundant) override {
unsigned sz = m_result_stack.size(); unsigned sz = m_result_stack.size();
(void)sz;
process(n, false, redundant); process(n, false, redundant);
SASSERT(m_result_stack.size() == sz + 1); SASSERT(m_result_stack.size() == sz + 1);
sat::literal result = m_result_stack.back(); sat::literal result = m_result_stack.back();

View file

@ -64,7 +64,8 @@ class sat_tactic : public tactic {
TRACE("sat_dimacs", m_solver->display_dimacs(tout);); TRACE("sat_dimacs", m_solver->display_dimacs(tout););
dep2assumptions(dep2asm, assumptions); dep2assumptions(dep2asm, assumptions);
lbool r = m_solver->check(assumptions.size(), assumptions.c_ptr()); lbool r = m_solver->check(assumptions.size(), assumptions.c_ptr());
TRACE("sat", tout << "result of checking: " << r << " " << m_solver->get_reason_unknown() << "\n";); TRACE("sat", tout << "result of checking: " << r << " ";
if (r == l_undef) tout << m_solver->get_reason_unknown(); tout << "\n";);
if (r == l_false) { if (r == l_false) {
expr_dependency * lcore = nullptr; expr_dependency * lcore = nullptr;
if (produce_core) { if (produce_core) {
@ -88,13 +89,18 @@ class sat_tactic : public tactic {
for (auto const& kv : map) { for (auto const& kv : map) {
expr * n = kv.m_key; expr * n = kv.m_key;
sat::bool_var v = kv.m_value; sat::bool_var v = kv.m_value;
if (!is_app(n))
continue;
app* a = to_app(n);
if (!is_uninterp_const(a))
continue;
TRACE("sat_tactic", tout << "extracting value of " << mk_ismt2_pp(n, m) << "\nvar: " << v << "\n";); TRACE("sat_tactic", tout << "extracting value of " << mk_ismt2_pp(n, m) << "\nvar: " << v << "\n";);
switch (sat::value_at(v, ll_m)) { switch (sat::value_at(v, ll_m)) {
case l_true: case l_true:
md->register_decl(to_app(n)->get_decl(), m.mk_true()); md->register_decl(a->get_decl(), m.mk_true());
break; break;
case l_false: case l_false:
md->register_decl(to_app(n)->get_decl(), m.mk_false()); md->register_decl(a->get_decl(), m.mk_false());
break; break;
default: default:
break; break;

View file

@ -20,6 +20,7 @@ endforeach()
add_executable(shell add_executable(shell
datalog_frontend.cpp datalog_frontend.cpp
dimacs_frontend.cpp dimacs_frontend.cpp
drat_frontend.cpp
"${CMAKE_CURRENT_BINARY_DIR}/gparams_register_modules.cpp" "${CMAKE_CURRENT_BINARY_DIR}/gparams_register_modules.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/install_tactic.cpp" "${CMAKE_CURRENT_BINARY_DIR}/install_tactic.cpp"
main.cpp main.cpp

197
src/shell/drat_frontend.cpp Normal file
View file

@ -0,0 +1,197 @@
/*++
Copyright (c) 2020 Microsoft Corporation
--*/
#include<iostream>
#include<fstream>
#include "util/memory_manager.h"
#include "util/statistics.h"
#include "sat/dimacs.h"
#include "sat/sat_solver.h"
#include "sat/sat_drat.h"
#include "smt/smt_solver.h"
#include "shell/drat_frontend.h"
#include "parsers/smt2/smt2parser.h"
#include "cmd_context/cmd_context.h"
class smt_checker {
ast_manager& m;
expr_ref_vector const& m_b2e;
expr_ref_vector m_fresh_exprs;
expr_ref_vector m_core;
params_ref m_params;
scoped_ptr<solver> m_solver;
expr* fresh(expr* e) {
unsigned i = e->get_id();
m_fresh_exprs.reserve(i + 1);
expr* r = m_fresh_exprs.get(i);
if (!r) {
r = m.mk_fresh_const("sk", m.get_sort(e));
m_fresh_exprs[i] = r;
}
return r;
}
expr_ref define(expr* e, unsigned depth) {
expr_ref r(fresh(e), m);
m_core.push_back(m.mk_eq(r, e));
if (depth == 0)
return r;
r = e;
if (is_app(e)) {
expr_ref_vector args(m);
for (expr* arg : *to_app(e))
args.push_back(define(arg, depth - 1));
r = m.mk_app(to_app(e)->get_decl(), args.size(), args.c_ptr());
}
return r;
}
void unfold1(sat::literal_vector const& lits) {
m_core.reset();
for (sat::literal lit : lits) {
expr* e = m_b2e[lit.var()];
expr_ref fml = define(e, 2);
if (!lit.sign())
fml = m.mk_not(fml);
m_core.push_back(fml);
}
}
public:
smt_checker(expr_ref_vector const& b2e):
m(b2e.m()), m_b2e(b2e), m_fresh_exprs(m), m_core(m) {
m_solver = mk_smt_solver(m, m_params, symbol());
}
void check_shallow(sat::literal_vector const& lits) {
unfold1(lits);
m_solver->push();
for (auto* c : m_core)
m_solver->assert_expr(c);
lbool is_sat = m_solver->check_sat();
m_solver->pop(1);
if (is_sat == l_true) {
std::cout << "did not verify: " << lits << "\n" << m_core << "\n";
for (sat::literal lit : lits) {
expr_ref e(m_b2e[lit.var()], m);
if (lit.sign())
e = m.mk_not(e);
std::cout << e << " ";
}
std::cout << "\n";
}
}
};
static void verify_smt(char const* drat_file, char const* smt_file) {
cmd_context ctx;
ctx.set_ignore_check(true);
ctx.set_regular_stream(std::cerr);
ctx.set_solver_factory(mk_smt_strategic_solver_factory());
if (smt_file) {
std::ifstream smt_in(smt_file);
if (!parse_smt2_commands(ctx, smt_in)) {
std::cerr << "could not read file " << smt_file << "\n";
return;
}
}
std::ifstream ins(drat_file);
dimacs::drat_parser drat(ins, std::cerr);
std::function<int(char const* read_theory)> read_theory = [&](char const* r) {
if (strcmp(r, "euf") == 0)
return 0;
return ctx.m().mk_family_id(symbol(r));
};
drat.set_read_theory(read_theory);
params_ref p;
reslimit lim;
p.set_bool("drat.check_unsat", true);
sat::solver solver(p, lim);
sat::drat drat_checker(solver);
drat_checker.updt_config();
expr_ref_vector bool_var2expr(ctx.m());
expr_ref_vector exprs(ctx.m()), args(ctx.m());
func_decl* f = nullptr;
ptr_vector<sort> sorts;
smt_checker checker(bool_var2expr);
auto check_smt = [&](dimacs::drat_record const& r) {
auto const& st = r.m_status;
if (st.is_input())
;
else if (st.is_sat() && st.is_asserted()) {
std::cout << "Tseitin tautology " << r;
checker.check_shallow(r.m_lits);
}
else if (st.is_sat())
;
else if (st.is_deleted())
;
else {
std::cout << "check smt " << r;
checker.check_shallow(r.m_lits);
// TBD: shallow check may fail because it doesn't include
// all RUP units, whish are sometimes required.
}
};
for (auto const& r : drat) {
std::cout << r;
std::cout.flush();
switch (r.m_tag) {
case dimacs::drat_record::tag_t::is_clause:
for (sat::literal lit : r.m_lits)
while (lit.var() >= solver.num_vars())
solver.mk_var(true);
drat_checker.add(r.m_lits, r.m_status);
check_smt(r);
break;
case dimacs::drat_record::tag_t::is_node:
args.reset();
sorts.reset();
for (auto n : r.m_args) {
args.push_back(exprs.get(n));
sorts.push_back(ctx.m().get_sort(args.back()));
}
if (r.m_name[0] == '(') {
std::cout << "parsing sexprs is TBD\n";
exit(0);
}
f = ctx.find_func_decl(symbol(r.m_name.c_str()), 0, nullptr, args.size(), sorts.c_ptr(), nullptr);
if (!f) {
std::cout << "could not find function\n";
exit(0);
}
exprs.reserve(r.m_node_id+1);
exprs.set(r.m_node_id, ctx.m().mk_app(f, args.size(), args.c_ptr()));
break;
case dimacs::drat_record::is_bool_def:
bool_var2expr.reserve(r.m_node_id+1);
bool_var2expr.set(r.m_node_id, exprs.get(r.m_args[0]));
break;
default:
UNREACHABLE();
break;
}
}
statistics st;
drat_checker.collect_statistics(st);
std::cout << st << "\n";
}
unsigned read_drat(char const* drat_file, char const* problem_file) {
if (!problem_file) {
std::cerr << "No smt2 file provided to checker\n";
return -1;
}
verify_smt(drat_file, problem_file);
return 0;
}

View file

@ -0,0 +1,8 @@
/*++
Copyright (c) 2011 Microsoft Corporation
--*/
#pragma once
unsigned read_drat(char const * drat_file, char const* problem_file);

View file

@ -38,15 +38,17 @@ Revision History:
#include "util/env_params.h" #include "util/env_params.h"
#include "util/file_path.h" #include "util/file_path.h"
#include "shell/lp_frontend.h" #include "shell/lp_frontend.h"
#include "shell/drat_frontend.h"
#if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) #if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) )
#include <crtdbg.h> #include <crtdbg.h>
#endif #endif
typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS } input_kind; typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS, IN_DRAT } input_kind;
static std::string g_aux_input_file; static std::string g_aux_input_file;
static char const * g_input_file = nullptr; static char const * g_input_file = nullptr;
static char const * g_drat_input_file = nullptr;
static bool g_standard_input = false; static bool g_standard_input = false;
static input_kind g_input_kind = IN_UNSPECIFIED; static input_kind g_input_kind = IN_UNSPECIFIED;
bool g_display_statistics = false; bool g_display_statistics = false;
@ -301,13 +303,15 @@ static void parse_cmd_line_args(int argc, char ** argv) {
gparams::set(key, value); gparams::set(key, value);
} }
else { else {
if (g_input_file) { if (get_extension(arg) && strcmp(get_extension(arg), "drat") == 0) {
g_input_kind = IN_DRAT;
g_drat_input_file = arg;
}
else if (g_input_file)
warning_msg("input file was already specified."); warning_msg("input file was already specified.");
} else
else {
g_input_file = arg; g_input_file = arg;
} }
}
i++; i++;
} }
@ -388,6 +392,9 @@ int STD_CALL main(int argc, char ** argv) {
case IN_MPS: case IN_MPS:
return_value = read_mps_file(g_input_file); return_value = read_mps_file(g_input_file);
break; break;
case IN_DRAT:
return_value = read_drat(g_drat_input_file, g_input_file);
break;
default: default:
UNREACHABLE(); UNREACHABLE();
} }

View file

@ -1,4 +1,3 @@
/*++ /*++
Copyright (c) 2015 Microsoft Corporation Copyright (c) 2015 Microsoft Corporation

View file

@ -68,7 +68,6 @@ z3_add_component(smt
theory_dl.cpp theory_dl.cpp
theory_dummy.cpp theory_dummy.cpp
theory_fpa.cpp theory_fpa.cpp
theory_jobscheduler.cpp
theory_lra.cpp theory_lra.cpp
theory_opt.cpp theory_opt.cpp
theory_pb.cpp theory_pb.cpp

View file

@ -37,7 +37,6 @@ Revision History:
#include "smt/theory_pb.h" #include "smt/theory_pb.h"
#include "smt/theory_fpa.h" #include "smt/theory_fpa.h"
#include "smt/theory_str.h" #include "smt/theory_str.h"
#include "smt/theory_jobscheduler.h"
namespace smt { namespace smt {
@ -123,8 +122,6 @@ namespace smt {
setup_UFLRA(); setup_UFLRA();
else if (m_logic == "LRA") else if (m_logic == "LRA")
setup_LRA(); setup_LRA();
else if (m_logic == "CSP")
setup_CSP();
else if (m_logic == "QF_FP") else if (m_logic == "QF_FP")
setup_QF_FP(); setup_QF_FP();
else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP") else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP")
@ -203,8 +200,6 @@ namespace smt {
setup_QF_DT(); setup_QF_DT();
else if (m_logic == "LRA") else if (m_logic == "LRA")
setup_LRA(); setup_LRA();
else if (m_logic == "CSP")
setup_CSP();
else else
setup_unknown(st); setup_unknown(st);
} }
@ -932,11 +927,6 @@ namespace smt {
m_context.register_plugin(alloc(smt::theory_seq, m_context)); m_context.register_plugin(alloc(smt::theory_seq, m_context));
} }
void setup::setup_CSP() {
setup_unknown();
m_context.register_plugin(alloc(smt::theory_jobscheduler, m_context));
}
void setup::setup_special_relations() { void setup::setup_special_relations() {
m_context.register_plugin(alloc(smt::theory_special_relations, m_context, m_manager)); m_context.register_plugin(alloc(smt::theory_special_relations, m_context, m_manager));
} }

File diff suppressed because it is too large Load diff

View file

@ -1,247 +0,0 @@
/*++
Copyright (c) 2018 Microsoft Corporation
Module Name:
theory_jobscheduling.h
Abstract:
Propagation solver for jobscheduling problems.
It relies on an external module to tighten bounds of
job variables.
Author:
Nikolaj Bjorner (nbjorner) 2018-09-08.
Revision History:
--*/
#pragma once
#include "util/uint_set.h"
#include "ast/csp_decl_plugin.h"
#include "ast/arith_decl_plugin.h"
#include "smt/smt_theory.h"
namespace smt {
typedef uint64_t time_t;
class theory_jobscheduler : public theory {
public:
typedef svector<symbol> properties;
protected:
struct job_resource {
unsigned m_resource_id; // id of resource
time_t m_capacity; // amount of resource to use
unsigned m_loadpct; // assuming loadpct
time_t m_finite_capacity_end;
properties m_properties;
job_resource(unsigned r, time_t cap, unsigned loadpct, time_t end, properties const& ps):
m_resource_id(r), m_capacity(cap), m_loadpct(loadpct), m_finite_capacity_end(end), m_properties(ps) {}
};
struct job_time {
unsigned m_job;
time_t m_time;
job_time(unsigned j, time_t time): m_job(j), m_time(time) {}
struct compare {
bool operator()(job_time const& jt1, job_time const& jt2) const {
return jt1.m_time < jt2.m_time;
}
};
};
struct job_info {
bool m_is_preemptable; // can job be pre-empted
vector<job_resource> m_resources; // resources allowed to run job.
u_map<unsigned> m_resource2index; // resource to index into vector
enode* m_job;
enode* m_start;
enode* m_end;
enode* m_job2resource;
bool m_is_bound;
job_info(): m_is_preemptable(false), m_job(nullptr), m_start(nullptr), m_end(nullptr), m_job2resource(nullptr), m_is_bound(false) {}
};
struct res_available {
unsigned m_loadpct;
time_t m_start;
time_t m_end;
properties m_properties;
res_available(unsigned load_pct, time_t start, time_t end, properties const& ps):
m_loadpct(load_pct),
m_start(start),
m_end(end),
m_properties(ps)
{}
struct compare {
bool operator()(res_available const& ra1, res_available const& ra2) const {
return ra1.m_start < ra2.m_start;
}
};
};
struct res_info {
unsigned_vector m_jobs; // jobs allocated to run on resource
vector<res_available> m_available; // time intervals where resource is available
enode* m_resource;
enode* m_makespan;
res_info(): m_resource(nullptr), m_makespan(nullptr) {}
};
ast_manager& m;
csp_util u;
arith_util a;
vector<job_info> m_jobs;
vector<res_info> m_resources;
unsigned_vector m_bound_jobs;
unsigned m_bound_qhead;
struct scope {
unsigned m_bound_jobs_lim;
unsigned m_bound_qhead;
};
svector<scope> m_scopes;
protected:
theory_var mk_var(enode * n) override;
bool internalize_atom(app * atom, bool gate_ctx) override;
bool internalize_term(app * term) override;
void assign_eh(bool_var v, bool is_true) override {}
void new_eq_eh(theory_var v1, theory_var v2) override;
void new_diseq_eh(theory_var v1, theory_var v2) override;
void push_scope_eh() override;
void pop_scope_eh(unsigned num_scopes) override;
final_check_status final_check_eh() override;
bool can_propagate() override;
void propagate() override;
public:
theory_jobscheduler(context& ctx);
~theory_jobscheduler() override {}
void display(std::ostream & out) const override;
void collect_statistics(::statistics & st) const override;
bool include_func_interp(func_decl* f) override;
void init_model(model_generator & m) override;
model_value_proc * mk_value(enode * n, model_generator & mg) override;
bool get_value(enode * n, expr_ref & r) override;
theory * mk_fresh(context * new_ctx) override;
res_info& ensure_resource(unsigned r);
job_info& ensure_job(unsigned j);
public:
// set up job/resource global constraints
void set_preemptable(unsigned j, bool is_preemptable);
void add_job_resource(unsigned j, unsigned r, unsigned loadpct, time_t cap, time_t end, properties const& ps);
void add_resource_available(unsigned r, unsigned max_loadpct, time_t start, time_t end, properties const& ps);
void add_done();
// assignments
time_t est(unsigned j); // earliest start time of job j
time_t lst(unsigned j); // last start time
time_t ect(unsigned j); // earliest completion time
time_t lct(unsigned j); // last completion time
time_t start(unsigned j); // start time of job j
time_t end(unsigned j); // end time of job j
time_t get_lo(expr* e);
time_t get_up(expr* e);
time_t get_value(expr* e);
unsigned resource(unsigned j); // resource of job j
// derived bounds
time_t ect(unsigned j, unsigned r, time_t start);
bool lst(unsigned j, unsigned r, time_t& t);
bool resource_available(job_resource const& jr, res_available const& ra) const;
bool first_available(job_resource const& jr, res_info const& ri, unsigned& idx) const;
bool last_available(job_resource const& jr, res_info const& ri, unsigned& idx) const;
time_t solve_for_start(unsigned load_pct, unsigned job_load_pct, time_t end, time_t cap);
time_t solve_for_end(unsigned load_pct, unsigned job_load_pct, time_t start, time_t cap);
time_t solve_for_capacity(unsigned load_pct, unsigned job_load_pct, time_t start, time_t end);
// validate assignment
void validate_assignment();
bool resource_available(unsigned r, time_t t, unsigned& idx); // load available on resource r at time t.
time_t capacity_used(unsigned j, unsigned r, time_t start, time_t end); // capacity used between start and end
job_resource const& get_job_resource(unsigned j, unsigned r) const;
bool job_has_resource(unsigned j, unsigned r) const;
// propagation
void propagate_end_time(unsigned j, unsigned r);
void propagate_job2resource(unsigned j, unsigned r);
// final check constraints
bool constrain_end_time_interval(unsigned j, unsigned r);
bool constrain_resource_energy(unsigned r);
bool split_job2resource(unsigned j);
void assert_last_end_time(unsigned j, unsigned r, job_resource const& jr, literal eq);
void assert_last_start_time(unsigned j, unsigned r, literal eq);
void assert_first_start_time(unsigned j, unsigned r, literal eq);
void assert_job_not_in_gap(unsigned j, unsigned r, unsigned idx, unsigned idx1, literal eq);
void assert_job_non_preemptable(unsigned j, unsigned r, unsigned idx, unsigned idx1, literal eq);
void block_job_overlap(unsigned r, uint_set const& jobs, unsigned last_job);
class job_overlap {
time_t m_start;
vector<job_time> & m_starts, &m_ends;
unsigned s_idx, e_idx; // index into starts/ends
uint_set m_jobs;
public:
job_overlap(vector<job_time>& starts, vector<job_time>& ends);
bool next();
uint_set const& jobs() const { return m_jobs; }
};
// term builders
literal mk_ge(expr* e, time_t t);
expr* mk_ge_expr(expr* e, time_t t);
literal mk_ge(enode* e, time_t t);
literal mk_le(expr* e, time_t t);
expr* mk_le_expr(expr* e, time_t t);
literal mk_le(enode* e, time_t t);
literal mk_le(enode* l, enode* r);
literal mk_literal(expr* e);
literal mk_eq_lit(enode * l, enode * r) { return mk_eq_lit(l->get_owner(), r->get_owner()); }
literal mk_eq_lit(expr * l, expr * r);
void internalize_cmd(expr* cmd);
void unrecognized_argument(expr* arg) { invalid_argument("unrecognized argument ", arg); }
void invalid_argument(char const* msg, expr* arg);
std::ostream& display(std::ostream & out, res_info const& r) const;
std::ostream& display(std::ostream & out, res_available const& r) const;
std::ostream& display(std::ostream & out, job_info const& r) const;
std::ostream& display(std::ostream & out, job_resource const& r) const;
};
};

View file

@ -120,7 +120,9 @@ static void test3() {
std::cout << g << "\n"; std::cout << g << "\n";
SASSERT(g.inconsistent()); SASSERT(g.inconsistent());
ptr_vector<int> js; ptr_vector<int> js;
g.begin_explain();
g.explain<int>(js); g.explain<int>(js);
g.end_explain();
for (int* j : js) { for (int* j : js) {
std::cout << "conflict: " << *j << "\n"; std::cout << "conflict: " << *j << "\n";
} }

View file

@ -18,6 +18,9 @@ Revision History:
--*/ --*/
#pragma once #pragma once
#if 0
-- unused
/** /**
Add element \c elem to the list headed by \c head. Add element \c elem to the list headed by \c head.
NextProc and PrevProc must have the methods: NextProc and PrevProc must have the methods:
@ -131,5 +134,52 @@ bool dlist_check_invariant(T * head, NextProc const & next = NextProc(), PrevPro
return true; return true;
} }
#endif
template<typename T>
class dll_base {
T* m_next { nullptr };
T* m_prev { nullptr };
public:
T* prev() { return m_prev; }
T* next() { return m_next; }
void init(T* t) {
m_next = t;
m_prev = t;
}
static void remove_from(T*& list, T* elem) {
if (list->m_next == list) {
SASSERT(elem == list);
list = nullptr;
return;
}
if (list == elem)
list = elem->m_next;
auto* next = elem->m_next;
auto* prev = elem->m_prev;
prev->m_next = next;
next->m_prev = prev;
}
static void push_to_front(T*& list, T* elem) {
if (!list) {
list = elem;
elem->m_next = elem;
elem->m_prev = elem;
}
else if (list != elem) {
remove_from(list, elem);
list->m_prev->m_next = elem;
elem->m_prev = list->m_prev;
elem->m_next = list;
list->m_prev = elem;
list = elem;
}
}
};

View file

@ -0,0 +1,36 @@
#include "util/vector.h"
#pragma once
class scoped_limit_trail {
unsigned_vector m_lim;
unsigned m_scopes{ 0 };
unsigned m_last{ 0 };
public:
void push(unsigned n) {
if (m_last == n)
m_scopes++;
else {
for (; m_scopes > 0; --m_scopes)
m_lim.push_back(m_last);
m_last = n;
}
}
unsigned pop(unsigned n) {
SASSERT(n > 0);
SASSERT(m_scopes + m_lim.size() >= n);
if (n <= m_scopes) {
m_scopes -= n;
return m_last;
}
else {
n -= m_scopes;
m_scopes = 0;
m_last = m_lim[m_lim.size() - n];
m_lim.shrink(m_lim.size() - n);
return m_last;
}
}
};

View file

@ -99,7 +99,7 @@ unsigned get_max_len(ptr_buffer<char> & keys) {
return max; return max;
} }
void statistics::display_smt2(std::ostream & out) const { std::ostream& statistics::display_smt2(std::ostream & out) const {
#define INIT_DISPLAY() \ #define INIT_DISPLAY() \
key2val m_u; \ key2val m_u; \
key2dval m_d; \ key2dval m_d; \
@ -140,9 +140,10 @@ void statistics::display_smt2(std::ostream & out) const {
} }
} }
out << ")\n"; out << ")\n";
return out;
} }
void statistics::display(std::ostream & out) const { std::ostream& statistics::display(std::ostream & out) const {
INIT_DISPLAY(); INIT_DISPLAY();
#undef DISPLAY_KEY #undef DISPLAY_KEY
@ -169,6 +170,7 @@ void statistics::display(std::ostream & out) const {
out << " " << std::fixed << std::setprecision(2) << d_val << "\n"; out << " " << std::fixed << std::setprecision(2) << d_val << "\n";
} }
} }
return out;
} }
template<typename M> template<typename M>

View file

@ -32,8 +32,8 @@ public:
void reset(); void reset();
void update(char const * key, unsigned inc); void update(char const * key, unsigned inc);
void update(char const * key, double inc); void update(char const * key, double inc);
void display(std::ostream & out) const; std::ostream& display(std::ostream & out) const;
void display_smt2(std::ostream & out) const; std::ostream& display_smt2(std::ostream & out) const;
void display_internal(std::ostream & out) const; void display_internal(std::ostream & out) const;
unsigned size() const; unsigned size() const;
bool is_uint(unsigned idx) const; bool is_uint(unsigned idx) const;
@ -42,6 +42,8 @@ public:
double get_double_value(unsigned idx) const; double get_double_value(unsigned idx) const;
}; };
inline std::ostream& operator<<(std::ostream& out, statistics const& st) { return st.display(out); }
void get_memory_statistics(statistics& st); void get_memory_statistics(statistics& st);
void get_rlimit_statistics(reslimit& l, statistics& st); void get_rlimit_statistics(reslimit& l, statistics& st);

View file

@ -36,10 +36,10 @@ private:
_trail_stack m_stack; _trail_stack m_stack;
}; };
template<typename Ctx = union_find_default_ctx> template<typename Ctx = union_find_default_ctx, typename StackCtx = Ctx>
class union_find { class union_find {
Ctx & m_ctx; Ctx & m_ctx;
trail_stack<Ctx> & m_trail_stack; trail_stack<StackCtx> & m_trail_stack;
svector<unsigned> m_find; svector<unsigned> m_find;
svector<unsigned> m_size; svector<unsigned> m_size;
svector<unsigned> m_next; svector<unsigned> m_next;
@ -47,12 +47,12 @@ class union_find {
class mk_var_trail; class mk_var_trail;
friend class mk_var_trail; friend class mk_var_trail;
class mk_var_trail : public trail<Ctx> { class mk_var_trail : public trail<StackCtx> {
union_find & m_owner; union_find & m_owner;
public: public:
mk_var_trail(union_find & o):m_owner(o) {} mk_var_trail(union_find & o):m_owner(o) {}
~mk_var_trail() override {} ~mk_var_trail() override {}
void undo(Ctx & ctx) override { void undo(StackCtx& ctx) override {
m_owner.m_find.pop_back(); m_owner.m_find.pop_back();
m_owner.m_size.pop_back(); m_owner.m_size.pop_back();
m_owner.m_next.pop_back(); m_owner.m_next.pop_back();
@ -64,13 +64,13 @@ class union_find {
class merge_trail; class merge_trail;
friend class merge_trail; friend class merge_trail;
class merge_trail : public trail<Ctx> { class merge_trail : public trail<StackCtx> {
union_find & m_owner; union_find & m_owner;
unsigned m_r1; unsigned m_r1;
public: public:
merge_trail(union_find & o, unsigned r1):m_owner(o), m_r1(r1) {} merge_trail(union_find & o, unsigned r1):m_owner(o), m_r1(r1) {}
~merge_trail() override {} ~merge_trail() override {}
void undo(Ctx & ctx) override { m_owner.unmerge(m_r1); } void undo(StackCtx& ctx) override { m_owner.unmerge(m_r1); }
}; };
void unmerge(unsigned r1) { void unmerge(unsigned r1) {