3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-06 01:24:08 +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_util.cpp
bv_decl_plugin.cpp
csp_decl_plugin.cpp
datatype_decl_plugin.cpp
decl_collector.cpp
display_dimacs.cpp

View file

@ -1342,6 +1342,17 @@ std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) {
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) {
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;
}
#ifdef Z3DEBUG
void pp(expr const * n, ast_manager & m) {
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);
};
std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p);
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, 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_bv2int);
vector<ptr_vector<func_decl> >::iterator it = m_bit2bool.begin();
vector<ptr_vector<func_decl> >::iterator end = m_bit2bool.end();
for (; it != end; ++it) {
ptr_vector<func_decl> & ds = *it;
for (auto& ds : m_bit2bool)
DEC_REF(ds);
}
DEC_REF(m_mkbv);
}
@ -829,6 +825,14 @@ bool bv_recognizers::is_bv2int(expr const* e, expr*& r) const {
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) {
if (n.is_one()) {
result = n;

View file

@ -368,6 +368,9 @@ public:
MATCH_BINARY(is_bv_sdivi);
MATCH_BINARY(is_bv_udivi);
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) 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_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_bit2bool(expr* e, unsigned idx) { parameter p(idx); return m_manager.mk_app(get_fid(), OP_BIT2BOOL, 1, &p, 1, &e); }
private:
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/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/ast_translation.h"
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) {
enode* r2 = r1->get_root();
r2->dec_class_size(r1->class_size());
@ -82,6 +41,9 @@ namespace euf {
enode* n = enode::mk(m_region, f, num_args, args);
m_nodes.push_back(n);
m_exprs.push_back(f);
push_node(n);
for (unsigned i = 0; i < num_args; ++i)
set_merge_enabled(args[i], true);
return n;
}
@ -104,8 +66,7 @@ namespace euf {
void egraph::reinsert_equality(enode* p) {
SASSERT(is_equality(p));
if (p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) {
m_new_lits.push_back(enode_bool_pair(p, true));
++m_stats.m_num_eqs;
add_literal(p, true);
}
}
@ -114,19 +75,14 @@ namespace euf {
}
void egraph::force_push() {
if (m_num_scopes == 0)
return;
for (; m_num_scopes > 0; --m_num_scopes) {
scope s;
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_scopes.push_back(m_updates.size());
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) {
@ -147,6 +103,7 @@ namespace euf {
return n;
if (is_equality(n)) {
update_children(n);
reinsert_equality(n);
return n;
}
enode_bool_pair p = m_table.insert(n);
@ -170,6 +127,18 @@ namespace euf {
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) {
force_push();
theory_var w = n->get_th_var(id);
@ -177,21 +146,37 @@ namespace euf {
if (w == null_theory_var) {
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) {
theory_var u = r->get_th_var(id);
if (u == null_theory_var)
r->add_th_var(v, id, m_region);
else
m_new_th_eqs.push_back(th_eq(id, v, u, n, r));
add_th_eq(id, v, u, n, r);
}
}
else {
theory_var u = r->get_th_var(id);
SASSERT(u != v && u != null_theory_var);
n->replace_th_var(v, id);
m_trail.push_back(new (m_region) replace_th_var_trail(n, id, u));
m_new_th_eqs.push_back(th_eq(id, v, u, n, r));
m_updates.push_back(update_record(n, id, u, update_record::replace_th_var()));
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;
unsigned old_lim = m_scopes.size() - num_scopes;
scope s = m_scopes[old_lim];
for (unsigned i = m_eqs.size(); i-- > s.m_num_eqs; ) {
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];
unsigned num_updates = m_scopes[old_lim];
auto undo_node = [&](enode* n) {
if (n->num_args() > 1)
m_table.erase(n);
m_expr2enode[n->get_owner_id()] = nullptr;
n->~enode();
}
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_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_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;
}
}
m_updates.shrink(num_updates);
m_scopes.shrink(old_lim);
m_region.pop_scope(num_scopes);
m_worklist.reset();
}
void egraph::merge(enode* n1, enode* n2, justification j) {
@ -245,8 +260,7 @@ namespace euf {
std::swap(n1, n2);
}
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));
++m_stats.m_num_lits;
add_literal(n1, false);
}
for (enode* p : enode_parents(n1))
m_table.erase(p);
@ -268,13 +282,13 @@ namespace euf {
for (auto iv : enode_th_vars(n)) {
theory_id id = iv.get_id();
theory_var v = root->get_th_var(id);
if (v == null_theory_var) {
root->add_th_var(iv.get_var(), id, m_region);
m_trail.push_back(new (m_region) add_th_var_trail(root, id));
if (v == null_theory_var) {
root->add_th_var(iv.get_var(), id, m_region);
m_updates.push_back(update_record(root, id, update_record::add_th_var()));
}
else {
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)
return;
m_inconsistent = true;
m_updates.push_back(update_record(false, update_record::inconsistent()));
m_n1 = n1;
m_n2 = n2;
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>
void egraph::explain(ptr_vector<T>& justifications) {
SASSERT(m_inconsistent);
SASSERT(m_todo.empty());
push_todo(m_n1);
push_todo(m_n2);
explain_eq(justifications, m_n1, m_n2, m_justification);
@ -399,7 +423,6 @@ namespace euf {
template <typename T>
void egraph::explain_eq(ptr_vector<T>& justifications, enode* a, enode* b) {
SASSERT(m_todo.empty());
SASSERT(a->get_root() == b->get_root());
enode* lca = find_lca(a, b);
push_to_lca(a, lca);
@ -418,9 +441,6 @@ namespace euf {
explain_eq(justifications, n, n->m_target, n->m_justification);
}
}
for (enode* n : m_todo)
n->unmark1();
m_todo.reset();
}
void egraph::invariant() {
@ -429,25 +449,29 @@ namespace euf {
}
std::ostream& egraph::display(std::ostream& out, unsigned max_args, enode* n) const {
out << std::setw(5)
<< n->get_owner_id() << " := ";
out << n->get_owner_id() << " := ";
if (!n->is_root())
out << "[" << n->get_root()->get_owner_id() << "] ";
expr* f = n->get_owner();
if (is_app(f))
out << to_app(f)->get_decl()->get_name() << " ";
out << mk_bounded_pp(f, m, 1);
else if (is_quantifier(f))
out << "q ";
out << "q:" << f->get_id();
else
out << "v ";
for (enode* arg : enode_args(n))
out << arg->get_owner_id() << " ";
for (unsigned i = n->num_args(); i < max_args; ++i)
out << " ";
out << "\t";
for (enode* p : enode_parents(n))
out << p->get_owner_id() << " ";
out << "v:" << f->get_id();
out << "\n";
if (!n->m_parents.empty()) {
out << " ";
for (enode* p : enode_parents(n))
out << p->get_owner_id() << " ";
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;
}
@ -464,8 +488,9 @@ namespace euf {
void egraph::collect_statistics(statistics& st) const {
st.update("euf merge", m_stats.m_num_merge);
st.update("euf conflicts", m_stats.m_num_conflicts);
st.update("euf eq prop", m_stats.m_num_eqs);
st.update("euf lit prop", m_stats.m_num_lits);
st.update("euf equality propagations", m_stats.m_num_eqs);
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) {
@ -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_eq(ptr_vector<int>& justifications, enode* a, enode* b);
template void euf::egraph::explain(ptr_vector<unsigned>& justifications);
template void euf::egraph::explain_todo(ptr_vector<unsigned>& justifications);
template void euf::egraph::explain_eq(ptr_vector<unsigned>& justifications, enode* a, enode* b);
template void euf::egraph::explain(ptr_vector<size_t>& justifications);
template void euf::egraph::explain_todo(ptr_vector<size_t>& justifications);
template void euf::egraph::explain_eq(ptr_vector<size_t>& justifications, enode* a, enode* b);

View file

@ -31,14 +31,6 @@ Notes:
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.
Theory 'id' is notified with the equality of theory variables v1, v2
@ -58,31 +50,66 @@ namespace euf {
class egraph {
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 {
unsigned m_num_merge;
unsigned m_num_th_eqs;
unsigned m_num_lits;
unsigned m_num_eqs;
unsigned m_num_conflicts;
stats() { reset(); }
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;
trail_stack m_trail;
region m_region;
enode_vector m_worklist;
etable m_table;
svector<add_eq_record> m_eqs;
svector<scope> m_scopes;
region m_region;
svector<update_record> m_updates;
unsigned_vector m_scopes;
enode_vector m_expr2enode;
enode_vector m_nodes;
expr_ref_vector m_exprs;
@ -101,9 +128,14 @@ namespace euf {
std::function<void(app*,app*)> m_used_cc;
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_add_th_var(enode* n, theory_id id);
enode* mk_enode(expr* f, unsigned num_args, enode * const* args);
void reinsert(enode* n);
void force_push();
@ -173,10 +205,13 @@ namespace euf {
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_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; }
void begin_explain();
void end_explain();
template <typename T>
void explain(ptr_vector<T>& justifications);
template <typename T>

View file

@ -36,21 +36,22 @@ namespace euf {
const theory_id null_theory_id = -1;
class enode {
expr* m_owner;
expr* m_owner{ nullptr };
bool m_mark1 { false };
bool m_mark2 { false };
bool m_commutative { false };
bool m_update_children { false };
bool m_interpreted { false };
bool m_merge_enabled { true };
unsigned m_class_size { 1 };
unsigned m_table_id { UINT_MAX };
enode_vector m_parents;
enode* m_next;
enode* m_root;
enode* m_next{ nullptr };
enode* m_root{ nullptr };
enode* m_target { nullptr };
th_var_list m_th_vars;
justification m_justification;
unsigned m_num_args;
unsigned m_num_args { 0 };
enode* m_args[0];
friend class enode_args;
@ -73,6 +74,7 @@ namespace euf {
n->m_root = n;
n->m_commutative = num_args == 2 && is_app(f) && to_app(f)->get_decl()->is_commutative();
n->m_num_args = num_args;
n->m_merge_enabled = true;
for (unsigned i = 0; i < num_args; ++i) {
SASSERT(to_app(f)->get_arg(i) == args[i]->get_owner());
n->m_args[i] = args[i];
@ -86,7 +88,8 @@ namespace euf {
friend class replace_th_var_trail;
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 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:
~enode() {
@ -106,6 +109,7 @@ namespace euf {
bool interpreted() const { return m_interpreted; }
bool commutative() const { return m_commutative; }
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]; }
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_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); }
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(); }
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) {}
instr_kind m_kind;
unsigned m_offset;
unsigned m_next;
app* m_app;
expr* m_pat;
unsigned m_reg;
unsigned m_other_reg;
unsigned m_count;
unsigned m_num_bound;
unsigned m_offset{ 0 };
unsigned m_next{ 0 };
app* m_app{ nullptr };
expr* m_pat{ nullptr };
unsigned m_reg{ 0 };
unsigned m_other_reg{ 0 };
unsigned m_count{ 0 };
unsigned m_num_bound{ 0 };
};
typedef obj_map<func_decl, unsigned> subst;

View file

@ -30,7 +30,6 @@ Notes:
#include "ast/seq_decl_plugin.h"
#include "ast/pb_decl_plugin.h"
#include "ast/fpa_decl_plugin.h"
#include "ast/csp_decl_plugin.h"
#include "ast/special_relations_decl_plugin.h"
#include "ast/ast_pp.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("fpa"), alloc(fpa_decl_plugin), logic_has_fpa());
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());
}
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("fpa"), logic_has_fpa(), 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) {
decl_plugin * p = m_manager->get_plugin(fid);

View file

@ -20,43 +20,21 @@ Revision History:
#undef max
#undef min
#include "sat/sat_solver.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; }
};
#include "sat/sat_drat.h"
template<typename Buffer>
void skip_whitespace(Buffer & in) {
while ((*in >= 9 && *in <= 13) || *in == 32) {
++in;
}
static bool is_whitespace(Buffer & in) {
return (*in >= 9 && *in <= 13) || *in == 32;
}
template<typename Buffer>
void skip_line(Buffer & in) {
static void skip_whitespace(Buffer & in) {
while (is_whitespace(in))
++in;
}
template<typename Buffer>
static void skip_line(Buffer & in) {
while(true) {
if (*in == EOF) {
return;
@ -70,7 +48,7 @@ void skip_line(Buffer & in) {
}
template<typename Buffer>
int parse_int(Buffer & in, std::ostream& err) {
static int parse_int(Buffer & in, std::ostream& err) {
int val = 0;
bool neg = false;
skip_whitespace(in);
@ -84,8 +62,11 @@ int parse_int(Buffer & in, std::ostream& err) {
}
if (*in < '0' || *in > '9') {
err << "(error, \"unexpected char: " << *in << " line: " << in.line() << "\")\n";
throw lex_error();
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";
throw dimacs::lex_error();
}
while (*in >= '0' && *in <= '9') {
@ -97,7 +78,7 @@ int parse_int(Buffer & in, std::ostream& err) {
}
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 var;
@ -116,7 +97,24 @@ void read_clause(Buffer & in, std::ostream& err, sat::solver & solver, sat::lite
}
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;
try {
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 true;
}
}
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);
}
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:
Nikolaj Bjorner (nbjorner) 2020-09-07
Add parser to consume extended DRAT format.
--*/
#pragma once
@ -22,4 +25,87 @@ Revision History:
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.
class node {
bool m_sign;
bool_op m_op;
uint64_t m_lut;
unsigned m_size;
unsigned m_offset;
bool m_sign{ false };
bool_op m_op{ no_op };
uint64_t m_lut{ 0 };
unsigned m_size{ 0 };
unsigned m_offset{ 0 };
public:
node():
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;
scoped_ptr<solver> s;
unsigned m_bin_clauses;
unsigned m_stopped_at;
unsigned m_bin_clauses{ 0 };
unsigned m_stopped_at{ 0 };
vector<clause_vector> m_use_list;
unsigned m_limit1, m_limit2;
unsigned m_limit1{ 0 }, m_limit2{ 0 };
bool_vector m_mark, m_mark2;
literal_vector m_must_candidates, m_may_candidates;
unsigned m_state;
unsigned m_state{ 0 };
void init_g() { m_state = 0x7; }
void g_add_binary(literal l1, literal l2, bool flip2);

View file

@ -6,10 +6,10 @@ Module Name:
sat_drat.cpp
Abstract:
Produce DRAT proofs.
Check them using a very simple forward checker
Check them using a very simple forward checker
that interacts with external plugins.
Author:
@ -24,19 +24,18 @@ Notes:
namespace sat {
drat::drat(solver& s):
drat::drat(solver& s) :
s(s),
m_out(nullptr),
m_bout(nullptr),
m_inconsistent(false),
m_num_add(0),
m_num_del(0),
m_check_unsat(false),
m_check_sat(false),
m_check(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;
m_out = alloc(std::ofstream, s.get_config().m_drat_file.str(), mode);
if (s.get_config().m_drat_binary) {
@ -61,7 +60,7 @@ namespace sat {
m_bout = nullptr;
}
void drat::updt_config() {
void drat::updt_config() {
m_check_unsat = s.get_config().m_drat_check_unsat;
m_check_sat = s.get_config().m_drat_check_sat;
m_check = m_check_unsat || m_check_sat;
@ -75,31 +74,41 @@ namespace sat {
out << "d";
else if (st.is_asserted())
out << "a";
else if (st.is_input())
out << "i";
if (!st.is_sat())
out << " " << m_theory[st.get_th()];
return out;
return out;
}
void drat::dump(unsigned n, literal const* c, status st) {
if (st.is_asserted() && !s.m_ext)
return;
if (m_activity && ((m_num_add % 1000) == 0))
if (m_activity && ((m_stats.m_num_add % 1000) == 0))
dump_activity();
char buffer[10000];
char digits[20]; // enough for storing unsigned
char* lastd = digits + sizeof(digits);
unsigned len = 0;
if (st.is_asserted()) {
buffer[len++] = 'a';
buffer[len++] = ' ';
buffer[len++] = ' ';
}
else if (st.is_deleted()) {
buffer[len++] = 'd';
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()) {
for (char ch : m_theory[st.get_th()])
@ -108,28 +117,28 @@ namespace sat {
}
for (unsigned i = 0; i < n; ++i) {
literal lit = c[i];
unsigned v = lit.var();
unsigned v = lit.var();
if (lit.sign()) buffer[len++] = '-';
char* d = lastd;
SASSERT(v > 0);
while (v > 0) {
while (v > 0) {
d--;
*d = (v % 10) + '0';
v /= 10;
SASSERT(d > digits);
}
SASSERT(len + lastd < sizeof(buffer) + d);
memcpy(buffer + len, d, lastd - d);
len += static_cast<unsigned>(lastd - d);
buffer[len++] = ' ';
if (len + 50 > sizeof(buffer)) {
m_out->write(buffer, len);
len = 0;
SASSERT(len + lastd < sizeof(buffer) + d);
memcpy(buffer + len, d, lastd - d);
len += static_cast<unsigned>(lastd - d);
buffer[len++] = ' ';
if (len + 50 > sizeof(buffer)) {
m_out->write(buffer, len);
len = 0;
}
}
buffer[len++] = '0';
buffer[len++] = '\n';
m_out->write(buffer, len);
}
buffer[len++] = '0';
buffer[len++] = '\n';
m_out->write(buffer, len);
}
@ -144,9 +153,9 @@ namespace sat {
void drat::bdump(unsigned n, literal const* c, status st) {
unsigned char ch = 0;
if (st.is_redundant())
ch = 'a';
ch = 'a';
else if (st.is_deleted())
ch = 'd';
ch = 'd';
else return;
char buffer[10000];
int len = 0;
@ -164,8 +173,7 @@ namespace sat {
m_bout->write(buffer, len);
len = 0;
}
}
while (v);
} while (v);
}
buffer[len++] = 0;
m_bout->write(buffer, len);
@ -188,7 +196,7 @@ namespace sat {
if (c[i] != last) {
out << c[i] << " ";
last = c[i];
}
}
}
out << "\n";
}
@ -198,7 +206,7 @@ namespace sat {
declare(l);
IF_VERBOSE(20, trace(verbose_stream(), 1, &l, st););
if (st.is_redundant()) {
if (st.is_redundant() && st.is_sat()) {
verify(1, &l);
}
if (st.is_deleted()) {
@ -213,17 +221,17 @@ namespace sat {
void drat::append(literal l1, literal l2, status st) {
TRACE("sat_drat", pp(tout, st) << " " << l1 << " " << l2 << "\n";);
declare(l1);
declare(l1);
declare(l2);
literal lits[2] = { l1, l2 };
IF_VERBOSE(20, trace(verbose_stream(), 2, lits, st););
if (st.is_deleted()) {
// noop
// don't record binary as deleted.
}
else {
if (st.is_redundant()) {
if (st.is_redundant() && st.is_sat()) {
verify(2, lits);
}
clause* c = m_alloc.mk_clause(2, lits, st.is_redundant());
@ -252,18 +260,18 @@ namespace sat {
(*m_out) << "b " << v << " " << n << " 0\n";
}
void drat::def_begin(unsigned n, symbol const& name) {
if (m_out)
(*m_out) << "n " << n << " " << name;
void drat::def_begin(unsigned n, std::string const& name) {
if (m_out)
(*m_out) << "e " << n << " " << name;
}
void drat::def_add_arg(unsigned arg) {
if (m_out)
if (m_out)
(*m_out) << " " << arg;
}
void drat::def_end() {
if (m_out)
if (m_out)
(*m_out) << " 0\n";
}
@ -300,12 +308,12 @@ namespace sat {
unsigned n = c.size();
IF_VERBOSE(20, trace(verbose_stream(), n, c.begin(), st););
if (st.is_redundant()) {
if (st.is_redundant() && st.is_sat()) {
verify(c);
}
m_status.push_back(st);
m_proof.push_back(&c);
m_proof.push_back(&c);
if (st.is_deleted()) {
if (n > 0) del_watch(c, c[0]);
if (n > 1) del_watch(c, c[1]);
@ -327,11 +335,11 @@ namespace sat {
}
}
switch (num_watch) {
case 0:
m_inconsistent = true;
case 0:
m_inconsistent = true;
break;
case 1:
assign_propagate(l1);
case 1:
assign_propagate(l1);
break;
default: {
SASSERT(num_watch == 2);
@ -345,7 +353,7 @@ namespace sat {
}
void drat::del_watch(clause& c, literal l) {
watch& w = m_watches[(~l).index()];
watch& w = m_watches[(~l).index()];
for (unsigned i = 0; i < w.size(); ++i) {
if (m_watched_clauses[w[i]].m_clause == &c) {
w[i] = w.back();
@ -368,7 +376,7 @@ namespace sat {
bool drat::is_drup(unsigned n, literal const* c) {
if (m_inconsistent || n == 0) return true;
unsigned num_units = m_units.size();
for (unsigned i = 0; !m_inconsistent && i < n; ++i) {
for (unsigned i = 0; !m_inconsistent && i < n; ++i) {
assign_propagate(~c[i]);
}
if (!m_inconsistent) {
@ -417,7 +425,7 @@ namespace sat {
}
svector<sat::solver::bin_clause> bin;
s.collect_bin_clauses(bin, true);
for (auto & b : bin) {
for (auto& b : bin) {
bool found = false;
if (m_assignment[b.first.var()] != (b.first.sign() ? l_true : l_false)) found = true;
if (m_assignment[b.second.var()] != (b.second.sign() ? l_true : l_false)) found = true;
@ -435,17 +443,16 @@ namespace sat {
}
m_units.shrink(num_units);
bool ok = m_inconsistent;
// IF_VERBOSE(9, verbose_stream() << "is-drup " << m_inconsistent << "\n");
m_inconsistent = false;
return ok;
}
bool drat::is_drat(unsigned n, literal const* c) {
if (m_inconsistent || n == 0) return true;
for (unsigned i = 0; i < n; ++i) {
if (is_drat(n, c, i)) return true;
}
if (m_inconsistent || n == 0)
return true;
for (unsigned i = 0; i < n; ++i)
if (is_drat(n, c, i))
return true;
return false;
}
@ -482,7 +489,7 @@ namespace sat {
if (st.is_sat() && j != c.size()) {
lits.append(j, c.begin());
lits.append(c.size() - j - 1, c.begin() + j + 1);
if (!is_drup(lits.size(), lits.c_ptr()))
if (!is_drup(lits.size(), lits.c_ptr()))
return false;
lits.resize(n);
}
@ -496,26 +503,33 @@ namespace sat {
if (!m_check_unsat) {
return;
}
for (unsigned i = 0; i < n; ++i) {
for (unsigned i = 0; i < n; ++i) {
declare(c[i]);
}
if (!is_drup(n, c) && !is_drat(n, c)) {
literal_vector lits(n, c);
IF_VERBOSE(0, verbose_stream() << "Verification of " << lits << " failed\n");
// s.display(std::cout);
std::string line;
std::getline(std::cin, line);
SASSERT(false);
INVOKE_DEBUGGER();
exit(0);
UNREACHABLE();
//display(std::cout);
TRACE("sat_drat",
tout << literal_vector(n, c) << "\n";
display(tout);
s.display(tout););
UNREACHABLE();
}
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);
IF_VERBOSE(0, verbose_stream() << "Verification of " << lits << " failed\n");
// s.display(std::cout);
std::string line;
std::getline(std::cin, line);
SASSERT(false);
INVOKE_DEBUGGER();
exit(0);
UNREACHABLE();
//display(std::cout);
TRACE("sat_drat",
tout << literal_vector(n, c) << "\n";
display(tout);
s.display(tout););
UNREACHABLE();
}
bool drat::contains(literal c, justification const& j) {
@ -529,7 +543,7 @@ namespace sat {
return contains(c, j.get_literal());
case justification::TERNARY:
return contains(c, j.get_literal1(), j.get_literal2());
case justification::CLAUSE:
case justification::CLAUSE:
return contains(s.get_clause(j));
default:
return true;
@ -546,7 +560,7 @@ namespace sat {
if (match(n, lits, c)) {
if (st.is_deleted()) {
num_del++;
}
}
else {
num_add++;
}
@ -578,7 +592,7 @@ namespace sat {
void drat::display(std::ostream& out) const {
out << "units: " << m_units << "\n";
for (unsigned i = 0; i < m_assignment.size(); ++i) {
lbool v = value(literal(i, false));
lbool v = value(literal(i, false));
if (v != l_undef) out << i << ": " << v << "\n";
}
for (unsigned i = 0; i < m_proof.size(); ++i) {
@ -599,12 +613,12 @@ namespace sat {
if (num_true == 0 && num_undef == 1) {
out << "Unit ";
}
pp(out, m_status[i]) << " " << i << ": " << *c << "\n";
pp(out, m_status[i]) << " " << i << ": " << *c << "\n";
}
}
for (unsigned i = 0; i < m_assignment.size(); ++i) {
watch const& w1 = m_watches[2*i];
watch const& w2 = m_watches[2*i + 1];
watch const& w1 = m_watches[2 * i];
watch const& w2 = m_watches[2 * i + 1];
if (!w1.empty()) {
out << i << " |-> ";
for (unsigned i = 0; i < w1.size(); ++i) out << *(m_watched_clauses[w1[i]].m_clause) << " ";
@ -626,7 +640,7 @@ namespace sat {
void drat::assign(literal l) {
lbool new_value = l.sign() ? l_false : l_true;
lbool old_value = value(l);
// TRACE("sat_drat", tout << "assign " << l << " := " << new_value << " from " << old_value << "\n";);
// TRACE("sat_drat", tout << "assign " << l << " := " << new_value << " from " << old_value << "\n";);
switch (old_value) {
case l_false:
m_inconsistent = true;
@ -645,7 +659,7 @@ namespace sat {
assign(l);
for (unsigned i = num_units; !m_inconsistent && i < m_units.size(); ++i) {
propagate(m_units[i]);
}
}
}
void drat::propagate(literal l) {
@ -676,10 +690,10 @@ namespace sat {
wc.m_l2 = lit;
m_watches[(~lit).index()].push_back(idx);
done = true;
}
}
}
if (done) {
continue;
continue;
}
else if (value(wc.m_l1) == l_false) {
m_inconsistent = true;
@ -693,19 +707,19 @@ namespace sat {
}
}
end_process_watch:
for (; it != end; ++it, ++it2)
*it2 = *it;
clauses.set_end(it2);
for (; it != end; ++it, ++it2)
*it2 = *it;
clauses.set_end(it2);
}
status drat::get_status(bool learned) const {
if (learned || s.m_searching)
return status::redundant();
return status::asserted();
return status::asserted();
}
void drat::add() {
++m_num_add;
++m_stats.m_num_add;
if (m_out) (*m_out) << "0\n";
if (m_bout) bdump(0, nullptr, status::redundant());
if (m_check_unsat) {
@ -713,7 +727,7 @@ namespace sat {
}
}
void drat::add(literal l, bool learned) {
++m_num_add;
++m_stats.m_num_add;
status st = get_status(learned);
if (m_out) dump(1, &l, st);
if (m_bout) bdump(1, &l, st);
@ -721,19 +735,19 @@ namespace sat {
}
void drat::add(literal l1, literal l2, status st) {
if (st.is_deleted())
++m_num_del;
++m_stats.m_num_del;
else
++m_num_add;
literal ls[2] = {l1, l2};
++m_stats.m_num_add;
literal ls[2] = { l1, l2 };
if (m_out) dump(2, ls, st);
if (m_bout) bdump(2, ls, st);
if (m_check) append(l1, l2, st);
}
void drat::add(clause& c, status st) {
if (st.is_deleted())
++m_num_del;
++m_stats.m_num_del;
else
++m_num_add;
++m_stats.m_num_add;
if (m_out) dump(c.size(), c.begin(), st);
if (m_bout) bdump(c.size(), c.begin(), st);
if (m_check) {
@ -743,9 +757,9 @@ namespace sat {
}
void drat::add(literal_vector const& lits, status st) {
if (st.is_deleted())
++m_num_del;
else
++m_num_add;
++m_stats.m_num_del;
else
++m_stats.m_num_add;
if (m_check) {
switch (lits.size()) {
case 0: add(); break;
@ -756,12 +770,12 @@ namespace sat {
break;
}
}
}
if (m_out)
}
if (m_out)
dump(lits.size(), lits.c_ptr(), st);
}
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_bout) bdump(c.size(), c.begin(), status::redundant());
if (m_check) {
@ -780,15 +794,15 @@ namespace sat {
}
void drat::del(literal l) {
++m_num_del;
++m_stats.m_num_del;
if (m_out) dump(1, &l, status::deleted());
if (m_bout) bdump(1, &l, status::deleted());
if (m_check_unsat) append(l, status::deleted());
}
void drat::del(literal l1, literal l2) {
++m_num_del;
literal ls[2] = {l1, l2};
++m_stats.m_num_del;
literal ls[2] = { l1, l2 };
if (m_out) dump(2, ls, status::deleted());
if (m_bout) bdump(2, ls, status::deleted());
if (m_check) append(l1, l2, status::deleted());
@ -806,26 +820,47 @@ namespace sat {
m_seen[lit.index()] = false;
}
#endif
++m_num_del;
++m_stats.m_num_del;
if (m_out) dump(c.size(), c.begin(), status::deleted());
if (m_bout) bdump(c.size(), c.begin(), status::deleted());
if (m_check) {
clause* c1 = m_alloc.mk_clause(c.size(), c.begin(), c.is_learned());
clause* c1 = m_alloc.mk_clause(c.size(), c.begin(), c.is_learned());
append(*c1, status::deleted());
}
}
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_bout) bdump(c.size(), c.begin(), status::deleted());
if (m_check) {
clause* c1 = m_alloc.mk_clause(c.size(), c.begin(), true);
clause* c1 = m_alloc.mk_clause(c.size(), c.begin(), true);
append(*c1, status::deleted());
}
}
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:
Assertion (trusted modulo internalizer):
Input assertion:
i <literal>* 0
Assertion (true modulo a theory):
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:
b <bool-var-id> <ast-node-id> 0
Definition of an ast node:
n <ast-node-id> <name> <ast-node-id>* 0
Definition of an expression (ast-node):
e <ast-node-id> <name> <ast-node-id>* 0
Theory lemma
<theory-id> <literal>* 0
Redundant clause (theory lemma if theory id is given)
[r [<theory-id>]] <literal>* 0
Available theories are:
- euf The theory lemma should be a consequence of congruence closure.
@ -45,8 +51,16 @@ Notes:
#include "sat_types.h"
namespace sat {
class justification;
class clause;
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 {
clause* m_clause;
literal m_l1, m_l2;
@ -64,10 +78,10 @@ namespace sat {
literal_vector m_units;
vector<watch> m_watches;
svector<lbool> m_assignment;
svector<std::string> m_theory;
vector<std::string> m_theory;
bool m_inconsistent;
unsigned m_num_add, m_num_del;
bool m_check_unsat, m_check_sat, m_check, m_activity;
stats m_stats;
void dump_activity();
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;
public:
drat(solver& s);
~drat();
@ -114,7 +129,7 @@ namespace sat {
void bool_def(bool_var v, unsigned n);
// 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_end();
@ -139,7 +154,11 @@ namespace sat {
bool contains(literal c, justification const& j);
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))) {
// 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()) {
std::cout << "DRAT\n";
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 init_search() {}
virtual bool propagate(literal l, ext_constraint_idx idx) = 0;
virtual bool unit_propagate() = 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 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 void asserted(literal l) = 0;
virtual check_result check() = 0;

View file

@ -1900,7 +1900,6 @@ namespace sat {
}
bool simplifier::try_eliminate(bool_var v) {
TRACE("sat_simplifier", tout << "processing: " << v << "\n";);
if (value(v) != l_undef)
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;

View file

@ -86,7 +86,7 @@ namespace sat {
m_cuber = nullptr;
m_local_search = nullptr;
m_mc.set_solver(this);
//mk_var(false, false);
mk_var(false, false);
}
solver::~solver() {
@ -138,7 +138,7 @@ namespace sat {
m_qhead = 0;
m_trail.reset();
m_scopes.reset();
//mk_var(false, false);
mk_var(false, false);
if (src.inconsistent()) {
set_conflict();
@ -353,7 +353,7 @@ namespace sat {
clause * solver::mk_clause_core(unsigned num_lits, literal * lits, sat::status st) {
bool redundant = st.is_redundant();
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;
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";);
@ -1103,6 +1103,11 @@ namespace sat {
}
}
wlist.set_end(it2);
if (m_ext) {
m_ext->unit_propagate();
if (inconsistent())
return false;
}
}
SASSERT(m_qhead == m_trail.size());
SASSERT(!m_inconsistent);
@ -1205,9 +1210,9 @@ namespace sat {
}
try {
init_search();
if (inconsistent()) return l_false;
if (check_inconsistent()) return l_false;
propagate(false);
if (inconsistent()) return l_false;
if (check_inconsistent()) return l_false;
init_assumptions(num_lits, lits);
propagate(false);
if (check_inconsistent()) return l_false;
@ -1644,7 +1649,9 @@ namespace sat {
bool solver::check_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();
return true;
}
@ -2576,13 +2583,6 @@ namespace sat {
if (m_step_size > m_config.m_step_size_min) {
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;
m_conflict_lvl = get_max_lvl(m_not_l, m_conflict, unique_max);
justification js = m_conflict;
@ -2609,7 +2609,6 @@ namespace sat {
pop_reinit(m_scope_lvl - m_conflict_lvl + 1);
m_force_conflict_analysis = true;
++m_stats.m_backtracks;
_reset.active = false;
return l_undef;
}
m_force_conflict_analysis = false;
@ -2685,7 +2684,7 @@ namespace sat {
break;
}
case justification::EXT_JUSTIFICATION: {
fill_ext_antecedents(consequent, js);
fill_ext_antecedents(consequent, js, false);
for (literal l : m_ext_antecedents)
process_antecedent(l, num_marks);
break;
@ -2786,6 +2785,7 @@ namespace sat {
if (m_par && 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";);
decay_activity();
updt_phase_counters();
@ -2847,7 +2847,7 @@ namespace sat {
break;
}
case justification::EXT_JUSTIFICATION: {
fill_ext_antecedents(consequent, js);
fill_ext_antecedents(consequent, js, true);
for (literal l : m_ext_antecedents) {
process_antecedent_for_unsat_core(l);
}
@ -2975,7 +2975,7 @@ namespace sat {
case justification::EXT_JUSTIFICATION:
if (not_l != null_literal)
not_l.neg();
fill_ext_antecedents(not_l, js);
fill_ext_antecedents(not_l, js, true);
for (literal l : m_ext_antecedents)
level = update_max_level(l, level, unique_max);
return level;
@ -3031,16 +3031,12 @@ namespace sat {
/**
\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(m_ext);
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->get_antecedents(consequent, idx, m_ext_antecedents);
m_cached_antecedent_consequent = consequent;
m_cached_antecedent_js = idx;
m_ext->get_antecedents(consequent, idx, m_ext_antecedents, probing);
}
bool solver::is_two_phase() const {
@ -3354,7 +3350,7 @@ namespace sat {
}
case justification::EXT_JUSTIFICATION: {
literal consequent(var, value(var) == l_false);
fill_ext_antecedents(consequent, js);
fill_ext_antecedents(consequent, js, false);
for (literal l : m_ext_antecedents) {
if (!process_antecedent_for_minimization(l)) {
reset_unmark(old_size);
@ -3496,7 +3492,7 @@ namespace sat {
break;
}
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) {
update_lrb_reasoned(l);
}
@ -3657,6 +3653,7 @@ namespace sat {
s.m_trail_lim = m_trail.size();
s.m_clauses_to_reinit_lim = m_clauses_to_reinit.size();
s.m_inconsistent = m_inconsistent;
// m_vars_lim.push(num_vars());
if (m_ext)
m_ext->push();
}
@ -3668,11 +3665,46 @@ namespace sat {
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) {
if (num_scopes == 0)
return;
if (m_ext)
if (m_ext) {
// pop_vars(num_scopes);
m_ext->pop(num_scopes);
}
SASSERT(num_scopes <= scope_lvl());
unsigned new_lvl = scope_lvl() - num_scopes;
scope & s = m_scopes[new_lvl];
@ -3709,12 +3741,27 @@ namespace sat {
m_qhead = m_trail.size();
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; ) {
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();
}
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) {
unsigned sz = m_clauses_to_reinit.size();
SASSERT(old_sz <= sz);
@ -3834,26 +3881,7 @@ namespace sat {
// v is an index of a variable that does not occur in solver state.
if (v < m_justification.size()) {
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();
shrink_vars(v);
}
}
@ -4766,7 +4794,7 @@ namespace sat {
break;
}
case justification::EXT_JUSTIFICATION: {
fill_ext_antecedents(lit, js);
fill_ext_antecedents(lit, js, true);
for (literal l : m_ext_antecedents) {
if (check_domain(lit, l) && all_found) {
s |= m_antecedents.find(l.var());

View file

@ -45,6 +45,7 @@ Revision History:
#include "util/trace.h"
#include "util/rlimit.h"
#include "util/scoped_ptr_vector.h"
#include "util/scoped_limit_trail.h"
namespace sat {
@ -172,6 +173,7 @@ namespace sat {
bool m_inconsistent;
};
svector<scope> m_scopes;
scoped_limit_trail m_vars_lim;
stopwatch m_stopwatch;
params_ref m_params;
scoped_ptr<solver> m_clone; // for debugging purposes
@ -399,6 +401,7 @@ namespace sat {
bool canceled() { return !m_rlimit.inc(); }
config const& get_config() const { return m_config; }
drat& get_drat() { return m_drat; }
drat* get_drat_ptr() override { return &m_drat; }
void set_incremental(bool b) { m_config.m_incremental = b; }
bool is_incremental() const { return m_config.m_incremental; }
extension* get_extension() const override { return m_ext.get(); }
@ -565,8 +568,6 @@ namespace sat {
unsigned m_conflict_lvl;
literal_vector m_lemma;
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 resolve_conflict();
lbool resolve_conflict_core();
@ -582,7 +583,7 @@ namespace sat {
void resolve_conflict_for_unsat_core();
void process_antecedent_for_unsat_core(literal antecedent);
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();
void updt_phase_of_vars();
void updt_phase_counters();
@ -621,6 +622,8 @@ namespace sat {
void push();
void pop(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 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(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:
void user_push() override;
void user_pop(unsigned num_scopes) override;
@ -798,4 +809,3 @@ namespace sat {
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());
if (ext) {
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);
}
else {

View file

@ -25,6 +25,7 @@ namespace sat {
class cut_simplifier;
class extension;
class drat;
class solver_core {
protected:
@ -95,6 +96,8 @@ namespace sat {
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
// to a set of assertions.

View file

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

View file

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

View file

@ -21,6 +21,10 @@ Author:
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) {
flet<bool> _redundant(m_is_redundant, redundant);
if (m_pb.is_pb(e))

View file

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

View file

@ -361,7 +361,7 @@ namespace sat {
void set_conflict(constraint& c, literal lit);
void assign(constraint& c, literal lit);
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_unit_propagation(constraint const& c, literal alit) const;
void validate_eliminated();
@ -576,8 +576,9 @@ namespace sat {
bool is_external(bool_var v) override;
bool propagate(literal l, ext_constraint_idx idx) override;
bool unit_propagate() override { return false; }
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;
check_result check() override;
void push() override;
@ -604,6 +605,7 @@ namespace sat {
bool check_model(model const& m) const 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;
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,23 +22,33 @@ Author:
namespace bv {
class add_var_pos_trail : public trail<euf::solver> {
solver::bit_atom * m_atom;
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;
public:
add_var_pos_trail(solver::bit_atom * a):m_atom(a) {}
void undo(euf::solver & euf) override {
add_var_pos_trail(solver::bit_atom* a) :m_atom(a) {}
void undo(euf::solver& euf) override {
SASSERT(m_atom->m_occs);
m_atom->m_occs = m_atom->m_occs->m_next;
}
};
class mk_atom_trail : public trail<euf::solver> {
class solver::mk_atom_trail : public trail<euf::solver> {
solver& th;
sat::bool_var m_var;
public:
mk_atom_trail(sat::bool_var v, solver& th):m_var(v), th(th) {}
void undo(euf::solver & euf) override {
solver::atom * a = th.get_bv2a(m_var);
mk_atom_trail(sat::bool_var v, solver& th) : th(th), m_var(v) {}
void undo(euf::solver& euf) override {
solver::atom* a = th.get_bv2a(m_var);
a->~atom();
th.erase_bv2a(m_var);
}
@ -49,162 +59,152 @@ namespace bv {
m_find.mk_var();
m_bits.push_back(sat::literal_vector());
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);
TRACE("bv", tout << "mk-var: " << r << " " << n->get_owner_id() << " " << mk_pp(n->get_owner(), m) << "\n";);
return r;
}
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
if (!visit_rec(m, e, sign, root, redundant))
return sat::null_literal;
return sat::null_literal;
void solver::apply_sort_cnstr(euf::enode * n, sort * s) {
get_var(n);
}
bool solver::visit(expr* e) {
if (!bv.is_bv(e)) {
ctx.internalize(e, false, false, m_is_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))
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) {
if (!is_app(e) || to_app(e)->get_family_id() != get_id()) {
ctx.internalize(e, m_is_redundant);
return true;
}
m_args.reset();
m_stack.push_back(sat::eframe(e));
return false;
}
bool solver::visited(expr* e) {
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);
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));
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);
}
if (m_args.size() != a->get_num_args())
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());
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;
default: break;
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;
}
bool solver::visited(expr* e) {
return get_enode(e) != nullptr;
}
void solver::mk_bits(theory_var v) {
TRACE("bv", tout << "v" << v << "\n";);
expr* e = get_expr(v);
expr* e = var2expr(v);
unsigned bv_size = get_bv_size(v);
literal_vector& bits = m_bits[v];
bits.reset();
m_bits[v].reset();
for (unsigned i = 0; i < bv_size; i++) {
expr_ref b2b = mk_bit2bool(e, i);
bits.push_back(ctx.internalize(b2b, false, false, m_is_redundant));
expr_ref b2b(bv.mk_bit2bool(e, i), m);
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) {
theory_var v = n->get_th_var(get_id());
if (v == euf::null_theory_var) {
v = mk_var(n);
mk_bits(v);
if (bv.is_bv(n->get_owner()))
mk_bits(v);
}
return v;
}
euf::enode* solver::get_arg(euf::enode* n, unsigned idx) {
if (get_config().m_bv_reflect) {
return n->get_arg(idx);
}
else {
expr* arg = n->get_arg(idx)->get_owner();
return ctx.get_enode(arg);
}
app* o = to_app(n->get_owner());
return ctx.get_enode(o->get_arg(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) {
literal_vector& bits = m_bits[v];
for (literal lit : bits) {
r.push_back(get_expr(lit));
}
for (literal lit : m_bits[v])
r.push_back(literal2expr(lit));
}
inline void solver::get_bits(euf::enode* n, expr_ref_vector& 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) {
app* arg = to_app(n->get_arg(idx));
get_bits(ctx.get_enode(arg), r);
@ -234,7 +228,7 @@ namespace bv {
void solver::register_true_false_bit(theory_var v, unsigned idx) {
SASSERT(s().value(m_bits[v][idx]) != l_undef);
bool is_true = (s().value(m_bits[v][idx]) == l_true);
zero_one_bits & bits = m_zero_one_bits[v];
zero_one_bits& bits = m_zero_one_bits[v];
bits.push_back(zero_one_bit(v, idx, is_true));
}
@ -242,18 +236,22 @@ namespace bv {
\brief Add bit l to the given variable.
*/
void solver::add_bit(theory_var v, literal l) {
literal_vector & bits = m_bits[v];
unsigned idx = bits.size();
bits.push_back(l);
if (s().value(l) != l_undef && s().lvl(l) == 0) {
unsigned idx = m_bits[v].size();
m_bits[v].push_back(l);
s().set_external(l.var());
set_bit_eh(v, l, 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 {
atom * a = get_bv2a(l.var());
SASSERT(!a || a->is_bit());
if (a) {
bit_atom* b = static_cast<bit_atom*>(a);
find_new_diseq_axioms(b->m_occs, v, idx);
atom* a = get_bv2a(l.var());
if (a && !a->is_bit())
;
else if (a) {
bit_atom* b = &a->to_bit();
find_new_diseq_axioms(*b, v, idx);
ctx.push(add_var_pos_trail(b));
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();
insert_bv2a(l.var(), b);
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);
}
}
}
void solver::add_unit(sat::literal lit) {
s().add_clause(1, &lit, status());
}
void solver::init_bits(euf::enode * n, expr_ref_vector const & bits) {
void solver::init_bits(expr* e, expr_ref_vector const& bits) {
euf::enode* n = expr2enode(e);
SASSERT(get_bv_size(n) == bits.size());
SASSERT(euf::null_theory_var != n->get_th_var(get_id()));
theory_var v = n->get_th_var(get_id());
unsigned sz = bits.size();
m_bits[v].reset();
for (expr* bit : bits)
add_bit(v, ctx.internalize(bit, false, false, m_is_redundant));
for (expr* bit : bits)
add_bit(v, get_literal(bit));
get_var(expr2enode(bit));
SASSERT(get_bv_size(n) == bits.size());
find_wpos(v);
}
@ -287,22 +284,23 @@ namespace bv {
}
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) {
numeral val;
unsigned sz = 0;
SASSERT(expr2enode(n)->interpreted());
VERIFY(bv.is_numeral(n, val, sz));
expr_ref_vector bits(m);
m_bb.num2bits(val, sz, bits);
literal_vector & c_bits = m_bits[v];
m_bb.num2bits(val, sz, bits);
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++) {
expr * l = bits.get(i);
expr* l = bits.get(i);
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);
}
fixed_var_eh(v);
@ -310,14 +308,12 @@ namespace bv {
void solver::internalize_mkbv(app* n) {
expr_ref_vector bits(m);
euf::enode * e = mk_enode(n, m_args);
bits.append(n->get_num_args(), n->get_args());
init_bits(e, bits);
init_bits(n, bits);
}
void solver::internalize_bv2int(app* n) {
mk_enode(n, m_args);
assert_bv2int_axiom(n);
assert_bv2int_axiom(n);
}
/**
@ -325,172 +321,270 @@ namespace bv {
* n = bv2int(k) = ite(bit2bool(k[sz-1],2^{sz-1},0) + ... + ite(bit2bool(k[0],1,0))
*/
void solver::assert_bv2int_axiom(app * n) {
expr* k = nullptr;
sort * int_sort = m.get_sort(n);
SASSERT(bv.is_bv2int(n, k));
void solver::assert_bv2int_axiom(app* n) {
expr* k = nullptr;
VERIFY(bv.is_bv2int(n, k));
SASSERT(bv.is_bv_sort(m.get_sort(k)));
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);
unsigned sz = bv.get_bv_size(k);
expr_ref_vector args(m);
expr_ref zero(bv.mk_numeral(numeral(0), int_sort), m);
numeral num(1);
for (expr* b : k_bits) {
expr_ref n(m_autil.mk_numeral(num, int_sort), m);
args.push_back(m.mk_ite(b, n, zero));
num *= numeral(2);
}
expr_ref zero(m_autil.mk_int(0), m);
unsigned i = 0;
for (expr* b : k_bits)
args.push_back(m.mk_ite(b, m_autil.mk_int(power2(i++)), zero));
expr_ref sum(m_autil.mk_add(sz, args.c_ptr()), m);
expr_ref eq(m.mk_eq(n, sum), m);
sat::literal lit = ctx.internalize(eq, false, false, m_is_redundant);
add_unit(lit);
}
void solver::internalize_int2bv(app* n) {
SASSERT(bv.is_int2bv(n));
euf::enode* e = mk_enode(n, m_args);
theory_var v = e->get_th_var(get_id());
mk_bits(v);
assert_int2bv_axiom(n);
euf::enode* e = expr2enode(n);
mk_bits(e->get_th_var(get_id()));
assert_int2bv_axiom(n);
}
/**
* create the axiom:
* bv2int(n) = e mod 2^bit_width
* bv2int(n) = e mod 2^bit_width
* where n = int2bv(e)
*
* Create the axioms:
* bit2bool(i,n) == ((e div 2^i) mod 2 != 0)
* for i = 0,.., sz-1
*/
void solver::assert_int2bv_axiom(app* n) {
SASSERT(bv.is_int2bv(n));
expr* e = n->get_arg(0);
euf::enode* n_enode = mk_enode(n, m_args);
parameter param(m_autil.mk_int());
expr* n_expr = n;
void solver::assert_int2bv_axiom(app* n) {
expr* e = nullptr;
VERIFY(bv.is_int2bv(n, e));
euf::enode* n_enode = expr2enode(n);
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);
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);
literal l = ctx.internalize(eq, false, false, m_is_redundant);
add_unit(l);
TRACE("bv", tout << eq << "\n";);
add_unit(ctx.internalize(eq, false, false, m_is_redundant));
expr_ref_vector n_bits(m);
get_bits(n_enode, n_bits);
for (unsigned i = 0; i < sz; ++i) {
numeral div = power(numeral(2), i);
mod = numeral(2);
rhs = m_autil.mk_idiv(e, m_autil.mk_numeral(div, true));
rhs = m_autil.mk_mod(rhs, m_autil.mk_numeral(mod, true));
rhs = m.mk_eq(rhs, m_autil.mk_numeral(rational(1), true));
lhs = n_bits.get(i);
numeral div = power2(i);
rhs = m_autil.mk_idiv(e, m_autil.mk_int(div));
rhs = m_autil.mk_mod(rhs, m_autil.mk_int(2));
rhs = m.mk_eq(rhs, m_autil.mk_int(1));
lhs = n_bits.get(i);
expr_ref eq(m.mk_eq(lhs, rhs), m);
TRACE("bv", tout << eq << "\n";);
l = ctx.internalize(eq, false, false, m_is_redundant);
add_unit(l);
add_unit(ctx.internalize(eq, false, false, m_is_redundant));
}
}
void solver::internalize_add(app* n) {}
void solver::internalize_sub(app* n) {}
void solver::internalize_mul(app* n) {}
void solver::internalize_udiv(app* n) {}
void solver::internalize_sdiv(app* n) {}
void solver::internalize_urem(app* n) {}
void solver::internalize_srem(app* n) {}
void solver::internalize_smod(app* n) {}
void solver::internalize_shl(app* n) {}
void solver::internalize_lshr(app* n) {}
void solver::internalize_ashr(app* n) {}
void solver::internalize_ext_rotate_left(app* 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) {}
}
#if 0
case OP_BADD: internalize_add(term); return true;
case OP_BSUB: internalize_sub(term); return true;
case OP_BMUL: internalize_mul(term); return true;
case OP_BSDIV_I: internalize_sdiv(term); return true;
case OP_BUDIV_I: internalize_udiv(term); return true;
case OP_BSREM_I: internalize_srem(term); return true;
case OP_BUREM_I: internalize_urem(term); return true;
case OP_BSMOD_I: internalize_smod(term); return true;
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;
template<bool Signed>
void solver::internalize_le(app* n) {
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 le(m);
if (Signed)
m_bb.mk_sle(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le);
else
m_bb.mk_ule(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le);
literal def = ctx.internalize(le, false, false, m_is_redundant);
add_def(def, expr2literal(n));
}
void solver::internalize_carry(app* n) {
SASSERT(n->get_num_args() == 3);
literal r = ctx.get_literal(n);
literal l1 = ctx.get_literal(n->get_arg(0));
literal l2 = ctx.get_literal(n->get_arg(1));
literal l3 = ctx.get_literal(n->get_arg(2));
add_clause(~r, l1, l2);
add_clause(~r, l1, l3);
add_clause(~r, l2, l3);
add_clause(r, ~l1, ~l2);
add_clause(r, ~l1, ~l3);
add_clause(r, ~l2, ~l3);
}
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:
bv_internalize.cpp
bv_solver.cpp
Abstract:
Internalize utilities for bit-vector solver plugin.
Solving utilities for bit-vectors.
Author:
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/euf_solver.h"
#include "sat/smt/sat_th.h"
@ -22,52 +24,110 @@ Author:
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;
}
/**
\brief Find an unassigned bit for m_wpos[v], if such bit cannot be found invoke fixed_var_eh
*/
void solver::find_wpos(theory_var v) {
literal_vector const & bits = m_bits[v];
unsigned sz = bits.size();
unsigned & wpos = m_wpos[v];
unsigned init = wpos;
for (; wpos < sz; wpos++) {
TRACE("find_wpos", tout << "curr bit: " << bits[wpos] << "\n";);
if (s().value(bits[wpos]) == l_undef) {
TRACE("find_wpos", tout << "moved wpos of v" << v << " to " << wpos << "\n";);
literal_vector const& bits = m_bits[v];
unsigned sz = bits.size();
unsigned& wpos = m_wpos[v];
for (unsigned i = 0; i < sz; ++i) {
unsigned idx = (i + wpos) % sz;
if (s().value(bits[idx]) == l_undef) {
wpos = idx;
TRACE("bv", tout << "moved wpos of v" << v << " to " << wpos << "\n";);
return;
}
}
wpos = 0;
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";);
TRACE("bv", tout << "v" << v << " is a fixed variable.\n";);
fixed_var_eh(v);
}
/**
\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) {
*\brief v[idx] = ~v'[idx], then v /= v' is a theory axiom.
*/
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];
l.neg();
while (occs) {
theory_var v2 = occs->m_var;
unsigned idx2 = occs->m_idx;
for (auto vp : a) {
theory_var v2 = vp.first;
unsigned idx2 = vp.second;
if (idx == idx2 && m_bits[v2][idx2] == l && get_bv_size(v2) == get_bv_size(v))
mk_new_diseq_axiom(v, v2, idx);
occs = occs->m_next;
}
}
/**
\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
return;
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++;
expr_ref eq(m.mk_eq(get_expr(v1), get_expr(v2)), m);
sat::literal neq = ctx.internalize(eq, true, false, m_is_redundant);
s().add_clause(1, &neq, sat::status::th(m_is_redundant, get_id()));
expr_ref eq(m.mk_eq(var2expr(v1), var2expr(v2)), m);
add_unit(~ctx.internalize(eq, false, false, m_is_redundant));
}
std::ostream& solver::display(std::ostream& out, theory_var v) const {
expr* e = var2expr(v);
out << "v";
out.width(4);
out << std::left << v;
out << " #";
out << " ";
out.width(4);
out << get_enode(v)->get_owner_id() << " -> #";
out << e->get_id() << " -> ";
out.width(4);
#if 0
out << get_enode(find(v))->get_owner_id();
out << std::right << ", bits:";
literal_vector const& bits = m_bits[v];
for (literal lit : bits) {
out << " " << lit << ":";
ctx.display_literal(out, lit);
out << var2enode(find(v))->get_owner_id();
out << std::right;
out.flush();
atom* a = nullptr;
if (is_bv(v)) {
numeral val;
if (get_fixed_value(v, val))
out << " (= " << val << ")";
for (literal lit : m_bits[v]) {
out << " " << lit << ":" << mk_bounded_pp(literal2expr(lit), m, 1);
}
}
numeral val;
if (get_fixed_value(v, val))
out << ", value: " << val;
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";
#endif
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; }
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::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) {}
sat::check_result solver::check() { return sat::check_result::CR_DONE; }
void solver::push() {}
void solver::pop(unsigned n) {}
void solver::get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) {
auto& c = bv_justification::from_index(idx);
TRACE("bv", display_constraint(tout, idx););
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::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() {}
lbool solver::get_phase(bool_var v) { return l_undef; }
std::ostream& solver::display(std::ostream& out) const { return out; }
std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const { return out; }
std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const { return out; }
void solver::collect_statistics(statistics& st) const {}
sat::extension* solver::copy(sat::solver* s) { return nullptr; }
std::ostream& solver::display(std::ostream& out) const {
unsigned num_vars = get_num_vars();
if (num_vars > 0)
out << "bv-solver:\n";
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() {}
bool solver::validate() { return true; }
void solver::init_use_list(sat::ext_use_list& ul) {}
bool solver::is_blocked(literal l, sat::ext_constraint_idx) { return false; }
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
#include "sat/smt/sat_th.h"
#include "sat/smt/bv_ackerman.h"
#include "ast/rewriter/bit_blaster/bit_blaster.h"
namespace euf {
class solver;
}
namespace bv {
class solver : public euf::th_euf_solver {
@ -32,15 +37,42 @@ namespace bv {
typedef std::pair<numeral, unsigned> value_sort_pair;
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 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 {
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)); }
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
@ -66,17 +98,31 @@ namespace bv {
typedef svector<zero_one_bit> zero_one_bits;
struct bit_atom;
struct def_atom;
class atom {
public:
virtual ~atom() {}
virtual bool is_bit() const = 0;
bit_atom& to_bit();
def_atom& to_def();
};
struct var_pos_occ {
theory_var m_var;
unsigned m_idx;
var_pos m_vp;
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 {
@ -84,44 +130,51 @@ namespace bv {
bit_atom():m_occs(nullptr) {}
~bit_atom() override {}
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_def;
le_atom(literal v, literal d):m_var(v), m_def(d) {}
~le_atom() override {}
def_atom(literal v, literal d):m_var(v), m_def(d) {}
~def_atom() override {}
bool is_bit() const override { return false; }
};
friend class add_var_pos_trail;
friend class mk_atom_trail;
class bit_trail;
class add_var_pos_trail;
class mk_atom_trail;
typedef ptr_vector<atom> bool_var2atom;
bv_util bv;
arith_util m_autil;
stats m_stats;
ackerman m_ackerman;
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.
ptr_vector<expr> m_bits_expr;
svector<unsigned> m_wpos; // per var, watch position for fixed variable detection.
unsigned_vector 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
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& s() { return *m_solver; }
// internalize:
// internalize
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; }
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 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(theory_var v);
theory_var get_var(euf::enode* n);
@ -129,66 +182,55 @@ namespace bv {
inline theory_var get_arg_var(euf::enode* n, unsigned idx);
void get_bits(theory_var v, 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);
euf::enode* mk_enode(expr* n, ptr_vector<euf::enode> const& args);
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()); }
void add_unit(sat::literal lit);
void register_true_false_bit(theory_var v, unsigned i);
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);
expr_ref mk_bit2bool(expr* b, unsigned idx);
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_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 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_concat(app * n);
void internalize_bv2int(app* n);
void internalize_int2bv(app* n);
void internalize_mkbv(app* n);
void internalize_umul_no_overflow(app *n);
void internalize_smul_no_overflow(app *n);
void internalize_smul_no_underflow(app *n);
void internalize_xor3(app* n);
void internalize_carry(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_int2bv_axiom(app* n);
void assert_ackerman(theory_var v1, theory_var v2);
// solving
theory_var find(theory_var v) const { return m_find.find(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);
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:
solver(euf::solver& ctx);
solver(euf::solver& ctx, theory_id id);
~solver() override {}
void set_solver(sat::solver* s) override { m_solver = s; }
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_external(bool_var v) 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;
sat::check_result check() override;
void push() override;
void pop(unsigned n) override;
void pre_simplify() override;
void simplify() override;
bool set_root(literal l, literal r) override;
void flush_roots() override;
void clauses_modifed() override;
lbool get_phase(bool_var v) override;
std::ostream& display(std::ostream& out) const override;
std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override;
std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override;
void collect_statistics(statistics& st) const override;
euf::th_solver* fresh(sat::solver* s, euf::solver& ctx) override;
extension* copy(sat::solver* s) override;
void find_mutexes(literal_vector& lits, vector<literal_vector> & mutexes) override {}
void gc() override {}
@ -220,18 +265,31 @@ namespace bv {
bool check_model(sat::model const& m) 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,
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; }
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;
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
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;
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); }

View file

@ -58,37 +58,10 @@ namespace euf {
inf.b = b;
inf.c = nullptr;
inf.is_cc = true;
inf.m_count = 0;
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() {
inference* inf = m_tmp_inference;
@ -97,15 +70,16 @@ namespace euf {
m.inc_ref(inf->a);
m.inc_ref(inf->b);
m.inc_ref(inf->c);
}
else
new_tmp();
}
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) {
remove_from_queue(inf);
inference::remove_from(m_queue, inf);
m_table.erase(inf);
m.dec_ref(inf->a);
m.dec_ref(inf->b);
@ -115,13 +89,10 @@ namespace euf {
void ackerman::new_tmp() {
m_tmp_inference = alloc(inference);
m_tmp_inference->m_next = m_tmp_inference->m_prev = m_tmp_inference;
m_tmp_inference->m_count = 0;
m_tmp_inference->init(m_tmp_inference);
}
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))
return;
app* a = to_app(n1);
@ -133,8 +104,6 @@ namespace euf {
}
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)
return;
insert(a, b, c);
@ -142,8 +111,6 @@ namespace euf {
}
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_num_args() == b->get_num_args());
insert(a, b);
@ -157,8 +124,8 @@ namespace euf {
m_num_propagations_since_last_gc = 0;
while (m_table.size() > m_gc_threshold)
remove(m_queue->m_prev);
remove(m_queue->prev());
m_gc_threshold *= 110;
m_gc_threshold /= 100;
m_gc_threshold++;
@ -171,13 +138,16 @@ namespace euf {
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());
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)
continue;
if (n->m_count >= m_high_watermark && num_prop < m_table.size())
++num_prop;
if (n->is_cc)
add_cc(n->a, n->b);
else
add_eq(n->a, n->b, n->c);
add_eq(n->a, n->b, n->c);
++s.m_stats.m_ackerman;
remove(n);
}
}

View file

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

View file

@ -21,17 +21,27 @@ Author:
namespace euf {
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
void solver::internalize(expr* e, bool redundant) {
if (si.is_bool_op(e))
return si.internalize(e, redundant);
auto* ext = get_solver(e);
if (ext)
return ext->internalize(e, sign, root, redundant);
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) {
if (si.is_bool_op(e))
return attach_lit(si.internalize(e, redundant), e);
if (auto* ext = get_solver(e))
return ext->internalize(e, sign, root, redundant);
if (!visit_rec(m, e, sign, root, redundant))
return sat::null_literal;
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) {
@ -39,15 +49,7 @@ namespace euf {
if (n)
return true;
if (si.is_bool_op(e)) {
sat::literal lit = si.internalize(e, m_is_redundant);
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());
attach_lit(si.internalize(e, m_is_redundant), e);
return true;
}
if (is_app(e) && to_app(e)->get_num_args() > 0) {
@ -64,8 +66,12 @@ namespace euf {
m_args.reset();
for (unsigned i = 0; i < num; ++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;
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());
attach_node(n);
return true;
@ -77,33 +83,45 @@ namespace euf {
void solver::attach_node(euf::enode* n) {
expr* e = n->get_owner();
log_node(n);
if (m.is_bool(e)) {
sat::bool_var v = si.add_bool_var(e);
log_bool_var(v, n);
attach_lit(literal(v, false), n);
if (!m.is_bool(e))
log_node(e);
else
attach_lit(literal(si.add_bool_var(e), false), e);
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);
}
void solver::attach_lit(literal lit, euf::enode* n) {
sat::literal solver::attach_lit(literal lit, expr* e) {
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);
s().mk_clause(~lit, lit2, sat::status::th(false, m.get_basic_family_id()));
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::asserted());
lit = lit2;
}
sat::bool_var v = lit.var();
m_var2node.reserve(v + 1, nullptr);
SASSERT(m_var2node[v] == nullptr);
m_var2node[v] = n;
m_var2expr.reserve(v + 1, nullptr);
SASSERT(m_var2expr[v] == nullptr);
m_var2expr[v] = e;
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)) {
enode_vector _args(m_args);
enode_vector _args(args);
if (sign)
add_not_distinct_axiom(e, _args.c_ptr());
else
@ -202,7 +220,7 @@ namespace euf {
expr* c = a->get_arg(0);
expr* th = a->get_arg(1);
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(!m.is_bool(e));
expr_ref eq_th(m.mk_eq(a, th), m);
@ -224,7 +242,7 @@ namespace euf {
}
}
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 lits1[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_ll_pp.h"
#include "sat/smt/euf_solver.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())
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);
switch (s().value(v)) {
case l_true:
@ -100,6 +101,8 @@ namespace euf {
expr* v = user_sort.get_fresh_value(m.get_sort(e));
values.set(id, v);
}
else if ((mb = get_solver(m.get_sort(e))))
mb->add_value(n, values);
else {
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))
continue;
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();
if (arity == 0)
mdl->register_decl(f, v);
@ -129,8 +135,11 @@ namespace euf {
mdl->register_decl(f, fi);
}
args.reset();
for (enode* arg : enode_args(n))
for (enode* arg : enode_args(n)) {
args.push_back(values.get(arg->get_root_id()));
SASSERT(args.back());
}
SASSERT(args.size() == arity);
if (!fi->get_entry(args.c_ptr()))
fi->insert_new_entry(args.c_ptr(), v);
}

View file

@ -19,53 +19,60 @@ Author:
namespace euf {
void solver::log_node(enode* n) {
if (m_drat) {
expr* e = n->get_owner();
if (is_app(e)) {
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();
}
else {
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());
void solver::init_drat() {
if (!m_drat_initialized)
get_drat().add_theory(m.get_basic_family_id(), symbol("euf"));
m_drat_initialized = true;
}
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 {
IF_VERBOSE(0, verbose_stream() << "logging binders is TBD\n");
}
}
/**
* \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) {
TRACE("euf", log_antecedents(tout, l, r););
if (m_drat) {
literal_vector lits;
for (literal lit : r) lits.push_back(~lit);
if (l != sat::null_literal)
lits.push_back(l);
s().get_drat().add(lits, sat::status::th(true, m.get_basic_family_id()));
}
if (!use_drat())
return;
literal_vector lits;
for (literal lit : r) lits.push_back(~lit);
if (l != sat::null_literal)
lits.push_back(l);
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) {
for (sat::literal l : r) {
enode* n = m_var2node[l.var()];
expr* n = m_var2expr[l.var()];
out << ~l << ": ";
if (!l.sign()) out << "! ";
out << mk_pp(n->get_owner(), m) << "\n";
out << mk_pp(n, m) << "\n";
SASSERT(s().value(l) == l_true);
}
if (l != sat::null_literal) {
out << l << ": ";
if (l.sign()) out << "! ";
enode* n = m_var2node[l.var()];
out << mk_pp(n->get_owner(), m) << "\n";
expr* n = m_var2expr[l.var()];
out << mk_pp(n, m) << "\n";
}
}

View file

@ -20,27 +20,25 @@ Author:
#include "sat/sat_solver.h"
#include "sat/smt/sat_smt.h"
#include "sat/smt/ba_solver.h"
#include "sat/smt/bv_solver.h"
#include "sat/smt/euf_solver.h"
namespace euf {
void solver::updt_params(params_ref const& 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.
*/
th_solver* solver::get_solver(sat::bool_var v) {
if (v >= m_var2node.size())
if (v >= m_var2expr.size())
return nullptr;
euf::enode* n = m_var2node[v];
if (!n)
expr* e = m_var2expr[v];
if (!e)
return nullptr;
return get_solver(n->get_owner());
return get_solver(e);
}
th_solver* solver::get_solver(expr* e) {
@ -49,8 +47,7 @@ namespace euf {
return nullptr;
}
th_solver* solver::get_solver(func_decl* f) {
family_id fid = f->get_family_id();
th_solver* solver::get_solver(family_id fid, func_decl* f) {
if (fid == null_family_id)
return nullptr;
auto* ext = m_id2solver.get(fid, nullptr);
@ -59,19 +56,24 @@ namespace euf {
if (fid == m.get_basic_family_id())
return nullptr;
pb_util pb(m);
bv_util bvu(m);
if (pb.get_family_id() == fid) {
ext = alloc(sat::ba_solver, *this, fid);
if (m_drat)
m_solver->get_drat().add_theory(fid, symbol("ba"));
if (use_drat())
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) {
ext->set_solver(m_solver);
ext->push_scopes(s().num_scopes());
add_solver(fid, ext);
}
else {
else if (f)
unhandled_function(f);
}
return ext;
}
@ -84,7 +86,7 @@ namespace euf {
if (m_unhandled_functions.contains(f))
return;
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");
}
@ -93,7 +95,7 @@ namespace euf {
}
bool solver::is_external(bool_var v) {
if (nullptr != m_var2node.get(v, nullptr))
if (nullptr != m_var2expr.get(v, nullptr))
return true;
for (auto* s : m_solvers)
if (s->is_external(v))
@ -107,86 +109,125 @@ namespace euf {
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);
if (ext == this)
get_antecedents(l, constraint::from_idx(idx), r);
get_antecedents(l, constraint::from_idx(idx), r, probing);
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) {
m_explain.reset();
void solver::add_antecedent(enode* a, enode* b) {
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;
// init_ackerman();
init_ackerman();
switch (j.kind()) {
case constraint::kind_t::conflict:
SASSERT(m_egraph.inconsistent());
m_egraph.explain<unsigned>(m_explain);
m_egraph.explain<size_t>(m_explain);
break;
case constraint::kind_t::eq:
n = m_var2node[l.var()];
e = m_var2expr[l.var()];
n = m_egraph.find(e);
SASSERT(n);
SASSERT(m_egraph.is_equality(n));
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;
case constraint::kind_t::lit:
n = m_var2node[l.var()];
e = m_var2expr[l.var()];
n = m_egraph.find(e);
SASSERT(n);
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;
default:
IF_VERBOSE(0, verbose_stream() << (unsigned)j.kind() << "\n");
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) {
auto* ext = get_solver(l.var());
if (ext) {
ext->asserted(l);
expr* e = m_var2expr.get(l.var(), nullptr);
if (!e) {
return;
}
bool sign = l.sign();
auto n = m_var2node.get(l.var(), nullptr);
bool sign = l.sign();
TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << " " << (sign ? "not ": " ") << e->get_id() << "\n";);
euf::enode* n = m_egraph.find(e);
if (!n)
return;
expr* e = n->get_owner();
if (m.is_eq(e) && !sign) {
for (auto th : enode_th_vars(n))
m_id2solver[th.get_id()]->asserted(l);
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* nb = n->get_arg(1);
TRACE("euf", tout << mk_pp(e, m) << "\n";);
m_egraph.merge(na, nb, base_ptr() + l.index());
m_egraph.merge(na, nb, c);
}
else {
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, base_ptr() + l.index());
m_egraph.merge(n, nb, c);
}
// TBD: delay propagation?
propagate();
}
void solver::propagate() {
while (m_egraph.propagate() && !s().inconsistent()) {
bool solver::unit_propagate() {
bool propagated = false;
while (!s().inconsistent()) {
if (m_egraph.inconsistent()) {
unsigned lvl = s().scope_lvl();
s().set_conflict(sat::justification::mk_ext_justification(lvl, conflict_constraint().to_index()));
return;
return true;
}
propagate_literals();
propagate_th_eqs();
bool propagated1 = false;
if (m_egraph.propagate()) {
propagate_literals();
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() {
@ -196,7 +237,7 @@ namespace euf {
bool is_eq = p.second;
expr* e = n->get_owner();
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));
size_t cnstr;
literal lit;
@ -207,7 +248,7 @@ namespace euf {
}
else {
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();
lit = literal(v, m.is_false(b));
}
@ -222,7 +263,7 @@ namespace euf {
void solver::propagate_th_eqs() {
for (; m_egraph.has_th_eq() && !s().inconsistent() && !m_egraph.inconsistent(); m_egraph.next_th_eq()) {
th_eq eq = m_egraph.get_th_eq();
m_id2solver[eq.m_id]->new_eq_eh(eq);
m_id2solver[eq.m_id]->new_eq_eh(eq);
}
}
@ -246,6 +287,7 @@ namespace euf {
}
sat::check_result solver::check() {
TRACE("euf", s().display(tout););
bool give_up = false;
bool cont = false;
for (auto* e : m_solvers)
@ -262,37 +304,52 @@ namespace euf {
}
void solver::push() {
si.push();
scope s;
s.m_var_lim = m_var_trail.size();
s.m_trail_lim = m_trail.size();
m_scopes.push_back(s);
m_region.push_scope();
m_trail.push_scope();
for (auto* e : m_solvers)
e->push();
m_egraph.push();
}
void solver::force_push() {
for (; m_num_scopes > 0; --m_num_scopes) {
}
}
void solver::pop(unsigned n) {
m_egraph.pop(n);
for (auto* e : m_solvers)
e->pop(n);
scope const & s = m_scopes[m_scopes.size() - n];
for (unsigned i = m_var_trail.size(); i-- > s.m_var_lim; )
m_var2node[m_var_trail[i]] = nullptr;
m_var_trail.shrink(s.m_var_lim);
undo_trail_stack(*this, m_trail, s.m_trail_lim);
m_region.pop_scope(n);
m_var2expr[m_var_trail[i]] = nullptr;
m_var_trail.shrink(s.m_var_lim);
m_trail.pop_scope(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() {
@ -319,13 +376,33 @@ namespace euf {
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 {
m_egraph.display(out);
out << "bool-vars\n";
for (unsigned v : m_var_trail) {
euf::enode* n = m_var2node[v];
out << v << ": " << n->get_owner_id() << " " << mk_bounded_pp(n->get_owner(), m, 1) << "\n";
expr* e = m_var2expr[v];
out << v << ": " << e->get_id() << " " << m_solver->value(v) << " " << mk_bounded_pp(e, m, 1) << "\n";
}
for (auto* e : m_solvers)
e->display(out);
@ -350,13 +427,19 @@ namespace euf {
m_egraph.collect_statistics(st);
for (auto* e : m_solvers)
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) {
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;
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->set_solver(s);
for (unsigned i = 0; i < m_id2solver.size(); ++i) {
@ -386,6 +469,9 @@ namespace euf {
for (auto* e : m_solvers)
if (!e->validate())
return false;
check_eqc_bool_assignment();
check_missing_bool_enode_propagation();
m_egraph.invariant();
return true;
}
@ -411,9 +497,9 @@ namespace euf {
unsigned solver::max_var(unsigned w) const {
for (auto* e : m_solvers)
w = e->max_var(w);
for (unsigned sz = m_var2node.size(); sz-- > 0; ) {
euf::enode* n = m_var2node[sz];
if (n && m.is_bool(n->get_owner())) {
for (unsigned sz = m_var2expr.size(); sz-- > 0; ) {
expr* n = m_var2expr[sz];
if (n && m.is_bool(n)) {
w = std::max(w, sz);
break;
}
@ -422,21 +508,15 @@ namespace euf {
}
double solver::get_reward(literal l, ext_constraint_idx idx, sat::literal_occs_fun& occs) const {
double r = 0;
for (auto* e : m_solvers) {
r = e->get_reward(l, idx, occs);
if (r != 0)
return r;
}
return r;
auto* ext = sat::constraint_base::to_extension(idx);
SASSERT(ext);
return (ext == this) ? 0 : ext->get_reward(l, idx, occs);
}
bool solver::is_extended_binary(ext_justification_idx idx, literal_vector& r) {
for (auto* e : m_solvers) {
if (e->is_extended_binary(idx, r))
return true;
}
return false;
auto* ext = sat::constraint_base::to_extension(idx);
SASSERT(ext);
return (ext != this) && ext->is_extended_binary(idx, r);
}
void solver::init_ackerman() {

View file

@ -48,41 +48,52 @@ namespace euf {
size_t to_index() const { return sat::constraint_base::mem2base(this); }
};
class solver : public sat::extension, public th_internalizer, public th_decompile {
typedef top_sort<euf::enode> deps_t;
friend class ackerman;
// friend class sat::ba_solver;
struct stats {
unsigned m_num_dynack;
unsigned m_ackerman;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
struct scope {
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;
atom2bool_var& m_expr2var;
sat::sat_internalizer& si;
smt_params m_config;
bool m_drat { false };
euf::egraph m_egraph;
euf_trail_stack m_trail;
stats m_stats;
region m_region;
func_decl_ref_vector m_unhandled_functions;
trail_stack m_trail;
sat::solver* m_solver { nullptr };
sat::lookahead* m_lookahead { nullptr };
ast_manager* m_to_m;
atom2bool_var* m_to_expr2var;
sat::sat_internalizer* m_to_si;
scoped_ptr<euf::ackerman> m_ackerman;
ptr_vector<euf::enode> m_var2node;
ptr_vector<unsigned> m_explain;
ptr_vector<expr> m_var2expr;
ptr_vector<size_t> m_explain;
unsigned m_num_scopes { 0 };
unsigned_vector m_var_trail;
svector<scope> m_scopes;
@ -93,26 +104,29 @@ namespace euf {
constraint* m_eq { nullptr };
constraint* m_lit { nullptr };
sat::solver& s() { return *m_solver; }
unsigned * base_ptr() { return reinterpret_cast<unsigned*>(this); }
// internalization
bool visit(expr* e) override;
bool visited(expr* e) override;
bool post_visit(expr* e, bool sign, bool root) override;
void attach_node(euf::enode* n);
void attach_lit(sat::literal lit, euf::enode* n);
bool post_visit(expr* e, bool sign, bool root) override;
sat::literal attach_lit(sat::literal lit, expr* e);
void add_distinct_axiom(app* e, euf::enode* const* args);
void add_not_distinct_axiom(app* e, euf::enode* const* args);
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_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
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(sat::bool_var v);
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);
// solving
void propagate();
void propagate_literals();
void propagate_th_eqs();
void get_antecedents(literal l, constraint& j, literal_vector& r);
void force_push();
void get_antecedents(literal l, constraint& j, literal_vector& r, bool probing);
// proofs
void log_antecedents(std::ostream& out, literal l, literal_vector const& r);
void log_antecedents(literal l, literal_vector const& r);
void log_node(enode* n);
void log_bool_var(sat::bool_var v, enode* n);
void log_node(expr* 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& 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); }
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_expr2var(expr2var),
si(si),
m_egraph(m),
m_trail(*this),
m_unhandled_functions(m),
m_solver(nullptr),
m_lookahead(nullptr),
m_to_m(&m),
m_to_expr2var(&expr2var),
m_to_si(&si)
m_to_si(&si),
m_reinit_exprs(m)
{
updt_params(p);
}
@ -166,37 +185,45 @@ namespace euf {
struct scoped_set_translate {
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.m_to_m = &m;
s.m_to_expr2var = &a2b;
s.m_to_si = &si;
}
~scoped_set_translate() {
s.m_to_m = &s.m;
s.m_to_expr2var = &s.m_expr2var;
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; }
ast_manager& get_manager() { return m; }
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; }
region& get_region() { return m_region; }
region& get_region() { return m_trail.get_region(); }
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 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 init_search() 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_external(bool_var v) 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;
sat::check_result check() override;
void push() override;
@ -220,6 +247,9 @@ namespace euf {
bool check_model(sat::model const& m) 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
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;
@ -228,11 +258,18 @@ namespace euf {
// internalize
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_node(euf::enode* n);
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);
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 bool is_bool_op(expr* e) const = 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 void cache(app* t, literal l) = 0;
virtual void push() = 0;
virtual void pop(unsigned n) = 0;
};
class constraint_base {

View file

@ -65,15 +65,24 @@ namespace euf {
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);
}
sat::literal th_euf_solver::get_literal(expr* e) const {
sat::literal th_euf_solver::expr2literal(expr* e) const {
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) {
force_push();
SASSERT(!is_attached_to_var(n));
euf::theory_var v = m_var2enode.size();
m_var2enode.push_back(n);
@ -82,7 +91,7 @@ namespace euf {
bool th_euf_solver::is_attached_to_var(enode* n) const {
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 {
@ -99,4 +108,35 @@ namespace euf {
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 visited(expr* e) { return false; }
virtual bool post_visit(expr* e, bool sign, bool root) { return false; }
public:
virtual ~th_internalizer() {}
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.
*/
@ -69,7 +72,7 @@ namespace euf {
/**
\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.
@ -102,24 +105,39 @@ namespace euf {
solver & ctx;
euf::enode_vector m_var2enode;
unsigned_vector m_var2enode_lim;
unsigned m_num_scopes{ 0 };
smt_params const& get_config() const;
sat::literal get_literal(expr* e) const;
sat::literal expr2literal(expr* e) const;
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:
th_euf_solver(euf::solver& ctx, euf::theory_id id);
virtual ~th_euf_solver() {}
virtual theory_var mk_var(enode * n);
unsigned get_num_vars() const { return m_var2enode.size();}
enode* get_enode(expr* e) const;
enode* get_enode(theory_var v) const { return m_var2enode[v]; }
expr* get_expr(theory_var v) const { return get_enode(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); }
enode* expr2enode(expr* e) const;
enode* var2enode(theory_var v) const { return m_var2enode[v]; }
expr* var2expr(theory_var v) const { return var2enode(v)->get_owner(); }
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(expr* e) const;
trail_stack<euf::solver> & get_trail_stack();
bool is_attached_to_var(enode* n) const;
bool is_root(theory_var v) const { return var2enode(v)->is_root(); }
void push() override;
void pop(unsigned n) override;
void 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 "ast/ast_smt2_pp.h"
#include "ast/ast_pp.h"
#include "ast/ast_smt2_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/pb_decl_plugin.h"
#include "ast/ast_util.h"
@ -38,6 +39,7 @@ Notes:
#include "tactic/tactic.h"
#include "tactic/generic_model_converter.h"
#include "sat/sat_cut_simplifier.h"
#include "sat/sat_drat.h"
#include "sat/tactic/goal2sat.h"
#include "sat/smt/ba_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_xor_solver;
bool m_euf;
bool m_drat;
bool m_is_redundant { false };
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_xor_solver = p.get_bool("xor_solver", false);
m_euf = sp.euf();
m_drat = sp.drat_file() != symbol();
}
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());
}
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() {
if (m_true == sat::null_literal) {
// 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
}
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 v = m_map.to_bool_var(t);
if (v == sat::null_bool_var) {
v = m_solver.add_var(true);
v = add_var(true, t);
force_push();
m_map.insert(t, v);
}
else {
@ -146,6 +202,28 @@ struct goal2sat::imp : public sat::sat_internalizer {
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 {
m_cache.insert(t, l);
}
@ -166,12 +244,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
strm << mk_ismt2_pp(t, m);
throw_op_not_handled(strm.str());
}
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";);
else {
if (!is_uninterp_const(t)) {
if (m_euf) {
convert_euf(t, root, sign);
@ -180,6 +253,12 @@ struct goal2sat::imp : public sat::sat_internalizer {
else
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 {
@ -191,7 +270,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
m_result_stack.reset();
SASSERT(l != sat::null_literal);
if (root)
mk_clause(l);
mk_root_clause(l);
else
m_result_stack.push_back(l);
}
@ -213,7 +292,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (sign)
l.neg();
if (root)
mk_clause(l);
mk_root_clause(l);
else
m_result_stack.push_back(l);
return true;
@ -275,17 +354,17 @@ struct goal2sat::imp : public sat::sat_internalizer {
for (unsigned i = 0; i < num; i++) {
sat::literal l = m_result_stack[i];
l.neg();
mk_clause(l);
mk_root_clause(l);
}
}
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();
}
else {
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);
m_cache.insert(t, l);
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) {
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 {
for (unsigned i = 0; i < num; ++i) {
mk_clause(m_result_stack[i]);
mk_root_clause(m_result_stack[i]);
}
}
m_result_stack.reset();
}
else {
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);
m_cache.insert(t, l);
sat::literal * lits = m_result_stack.end() - num;
@ -373,17 +452,17 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (root) {
SASSERT(sz == 3);
if (sign) {
mk_clause(~c, ~t);
mk_clause(c, ~e);
mk_root_clause(~c, ~t);
mk_root_clause(c, ~e);
}
else {
mk_clause(~c, t);
mk_clause(c, e);
mk_root_clause(~c, t);
mk_root_clause(c, e);
}
m_result_stack.reset();
}
else {
sat::bool_var k = m_solver.add_var(false);
sat::bool_var k = add_var(false, n);
sat::literal l(k, false);
m_cache.insert(n, l);
mk_clause(~l, ~c, t);
@ -411,16 +490,16 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (root) {
SASSERT(sz == 2);
if (sign) {
mk_clause(l1);
mk_clause(~l2);
mk_root_clause(l1);
mk_root_clause(~l2);
}
else {
mk_clause(~l1, l2);
mk_root_clause(~l1, l2);
}
m_result_stack.reset();
}
else {
sat::bool_var k = m_solver.add_var(false);
sat::bool_var k = add_var(false, t);
sat::literal l(k, false);
m_cache.insert(t, l);
// l <=> (l1 => l2)
@ -443,17 +522,17 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (root) {
SASSERT(sz == 2);
if (sign) {
mk_clause(l1, l2);
mk_clause(~l1, ~l2);
mk_root_clause(l1, l2);
mk_root_clause(~l1, ~l2);
}
else {
mk_clause(l1, ~l2);
mk_clause(~l1, l2);
mk_root_clause(l1, ~l2);
mk_root_clause(~l1, l2);
}
m_result_stack.reset();
}
else {
sat::bool_var k = m_solver.add_var(false);
sat::bool_var k = add_var(false, t);
sat::literal l(k, false);
m_cache.insert(t, l);
mk_clause(~l, l1, ~l2);
@ -486,7 +565,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
sat::extension* ext = m_solver.get_extension();
euf::solver* euf = nullptr;
if (!ext) {
euf = alloc(euf::solver, m, m_map, *this);
euf = alloc(euf::solver, m, *this);
m_solver.set_extension(euf);
for (unsigned i = m_solver.num_scopes(); i-- > 0; )
euf->push();
@ -502,7 +581,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (lit == sat::null_literal)
return;
if (root)
mk_clause(lit);
mk_root_clause(lit);
else
m_result_stack.push_back(lit);
}
@ -528,7 +607,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (lit == sat::null_literal)
return;
if (root)
mk_clause(lit);
mk_root_clause(lit);
else
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 {
unsigned sz = m_result_stack.size();
(void)sz;
process(n, false, redundant);
SASSERT(m_result_stack.size() == sz + 1);
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););
dep2assumptions(dep2asm, assumptions);
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) {
expr_dependency * lcore = nullptr;
if (produce_core) {
@ -88,13 +89,18 @@ class sat_tactic : public tactic {
for (auto const& kv : map) {
expr * n = kv.m_key;
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";);
switch (sat::value_at(v, ll_m)) {
case l_true:
md->register_decl(to_app(n)->get_decl(), m.mk_true());
md->register_decl(a->get_decl(), m.mk_true());
break;
case l_false:
md->register_decl(to_app(n)->get_decl(), m.mk_false());
md->register_decl(a->get_decl(), m.mk_false());
break;
default:
break;

View file

@ -20,6 +20,7 @@ endforeach()
add_executable(shell
datalog_frontend.cpp
dimacs_frontend.cpp
drat_frontend.cpp
"${CMAKE_CURRENT_BINARY_DIR}/gparams_register_modules.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/install_tactic.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/file_path.h"
#include "shell/lp_frontend.h"
#include "shell/drat_frontend.h"
#if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) )
#include <crtdbg.h>
#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 char const * g_input_file = nullptr;
static char const * g_drat_input_file = nullptr;
static bool g_standard_input = false;
static input_kind g_input_kind = IN_UNSPECIFIED;
bool g_display_statistics = false;
@ -301,12 +303,14 @@ static void parse_cmd_line_args(int argc, char ** argv) {
gparams::set(key, value);
}
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.");
}
else {
else
g_input_file = arg;
}
}
i++;
}
@ -361,7 +365,7 @@ int STD_CALL main(int argc, char ** argv) {
g_input_kind = IN_MPS;
}
}
}
}
switch (g_input_kind) {
case IN_SMTLIB_2:
memory::exit_when_out_of_memory(true, "(error \"out of memory\")");
@ -388,6 +392,9 @@ int STD_CALL main(int argc, char ** argv) {
case IN_MPS:
return_value = read_mps_file(g_input_file);
break;
case IN_DRAT:
return_value = read_drat(g_drat_input_file, g_input_file);
break;
default:
UNREACHABLE();
}

View file

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

View file

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

View file

@ -37,7 +37,6 @@ Revision History:
#include "smt/theory_pb.h"
#include "smt/theory_fpa.h"
#include "smt/theory_str.h"
#include "smt/theory_jobscheduler.h"
namespace smt {
@ -123,8 +122,6 @@ namespace smt {
setup_UFLRA();
else if (m_logic == "LRA")
setup_LRA();
else if (m_logic == "CSP")
setup_CSP();
else if (m_logic == "QF_FP")
setup_QF_FP();
else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP")
@ -203,8 +200,6 @@ namespace smt {
setup_QF_DT();
else if (m_logic == "LRA")
setup_LRA();
else if (m_logic == "CSP")
setup_CSP();
else
setup_unknown(st);
}
@ -932,11 +927,6 @@ namespace smt {
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() {
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";
SASSERT(g.inconsistent());
ptr_vector<int> js;
g.begin_explain();
g.explain<int>(js);
g.end_explain();
for (int* j : js) {
std::cout << "conflict: " << *j << "\n";
}

View file

@ -18,6 +18,9 @@ Revision History:
--*/
#pragma once
#if 0
-- unused
/**
Add element \c elem to the list headed by \c head.
NextProc and PrevProc must have the methods:
@ -131,5 +134,52 @@ bool dlist_check_invariant(T * head, NextProc const & next = NextProc(), PrevPro
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;
}
void statistics::display_smt2(std::ostream & out) const {
std::ostream& statistics::display_smt2(std::ostream & out) const {
#define INIT_DISPLAY() \
key2val m_u; \
key2dval m_d; \
@ -140,9 +140,10 @@ void statistics::display_smt2(std::ostream & out) const {
}
}
out << ")\n";
return out;
}
void statistics::display(std::ostream & out) const {
std::ostream& statistics::display(std::ostream & out) const {
INIT_DISPLAY();
#undef DISPLAY_KEY
@ -169,6 +170,7 @@ void statistics::display(std::ostream & out) const {
out << " " << std::fixed << std::setprecision(2) << d_val << "\n";
}
}
return out;
}
template<typename M>

View file

@ -32,8 +32,8 @@ public:
void reset();
void update(char const * key, unsigned inc);
void update(char const * key, double inc);
void display(std::ostream & out) const;
void display_smt2(std::ostream & out) const;
std::ostream& display(std::ostream & out) const;
std::ostream& display_smt2(std::ostream & out) const;
void display_internal(std::ostream & out) const;
unsigned size() const;
bool is_uint(unsigned idx) const;
@ -42,6 +42,8 @@ public:
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_rlimit_statistics(reslimit& l, statistics& st);

View file

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